iOS RN启动中管理Native Module详解

Hana ·
更新时间:2024-09-20
· 574 次阅读

目录

1. 全局的 native module 注册表

2. RCTBridgeModule 协议 

3. RCTModuleClasses中Class数据的处理

4. ModuleClasse包装成RCTModuleData过程

5. RCTModuleData在什么时候进行module instance

5. RCTModuleData在进行module instance的细节

1. 全局的 native module 注册表

RCTModuleClasses 数组

首先, RN中拥有一个全局的静态数组RCTModuleClasses, 使用它来记录所有的Native Module的Class, 可以认为这是一个待启动的Native Module配置表!!!

并且提供了一个全局方法void RCTRegisterModule(Class);, 向这个RCTModuleClasses注入新的module class:

// 1. 全局注册中心, RN 用 RCTModuleClasses 来持有全局注册的 native module static NSMutableArray<Class> *RCTModuleClasses; static dispatch_queue_t RCTModuleClassesSyncQueue; // 2. sync 读 - 全局 RCTModuleClasses NSArray<Class> *RCTGetModuleClasses(void) { __block NSArray<Class> *result; dispatch_sync(RCTModuleClassesSyncQueue, ^{ result = [RCTModuleClasses copy]; }); return result; } // 3. barrier async 写 void RCTRegisterModule(Class moduleClass) { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ RCTModuleClasses = [NSMutableArray new]; RCTModuleClassesSyncQueue = dispatch_queue_create("com.facebook.react.ModuleClassesSyncQueue", DISPATCH_QUEUE_CONCURRENT); // sync 读, barrier_async 写 }); dispatch_barrier_async(RCTModuleClassesSyncQueue, ^{ [RCTModuleClasses addObject:moduleClass]; }); } 2. RCTBridgeModule 协议 

APP在pre main将module注入全局表中

RN中如果要实现一个native module, 需要实现RCTBridgeModule协议, 并且RN官方要求我们在实现协议时, 必须加入RCT_EXPORT_MODULE宏.

@protocol RCTBridgeModule <NSObject> /** * Place this macro in your class implementation to automatically register * your module with the bridge when it loads. The optional js_name argument * will be used as the JS module name. If omitted, the JS module name will * match the Objective-C class name. */ #define RCT_EXPORT_MODULE(js_name) \ RCT_EXTERN void RCTRegisterModule(Class); \ +(NSString *)moduleName \ { \ return @ #js_name; \ } \ +(void)load \ { \ RCTRegisterModule(self); \ } ... @end

其中RCT_EXPORT_MODULE(js_name)宏的实现中, 会在+load中调用RCTRegisterModule(self); 方法, 将Native Module Class注入到全局的静态数组RCTModuleClasses中!!!

也就是说, iOS APP在启动执行main函数之前, 所有的实现RCTBridgeModule协议的类型Class, 都会被注入到RCTModuleClasses这个数组中.

3. RCTModuleClasses中Class数据的处理

RCTCxxbridge的start方法执行时, RCTGetModuleClasses()方法里面已经拥有了所有需要注册到bridge的native module配置表, 因此直接可以对这个module配置表中, 所有的 class进行初始化!!!

//(void)[self _initializeModules:RCTGetModuleClasses() withDispatchGroup:prepareBridge lazilyDiscovered:NO]; - (NSArray<RCTModuleData *> *)_initializeModules:(NSArray<Class> *)modules withDispatchGroup:(dispatch_group_t)dispatchGroup lazilyDiscovered:(BOOL)lazilyDiscovered { /// 1. modules Class => 包装成中间管理类 RCTModuleData /// 2. 然后将 RCTModuleData 对象放到 bridge 的几个特定容器中管理 NSArray<RCTModuleData *> *moduleDataById = [self _registerModulesForClasses:modules lazilyDiscovered:lazilyDiscovered]; if (lazilyDiscovered) { ... } else { // 初始化时候, 会走这里 /// 3. 处理 moduleData.hasInstance 的场景, 在 bridge._moduleSetupComplete 之前, module强制进行instance实例化, 并管理在 moduleData 中 for (RCTModuleData *moduleData in _moduleDataByID) { if (moduleData.hasInstance && (!moduleData.requiresMainQueueSetup || RCTIsMainQueue())) { (void)[moduleData instance]; } } /// 4. 标记 bridge 中的 module 完成 setup 工作 _moduleSetupComplete = YES; /// 5. 对 bridge 中的 module 异步 preapre [self _prepareModulesWithDispatchGroup:dispatchGroup]; } /// 6. 返回所有的 moduleData 数组 return moduleDataById; }

主要来说是做以下几个事情:

_registerModulesForClasses, 主要工作: Module Class包装成RCTModuleData:

将每个moduleClass封装成一个个RCTModuleData 对象moduleData

然后对moduleData进行setUp()

将RCTModuleData注册到bridge的module管理的属性中:

NSMutableDictionary<NSString *, RCTModuleData *> *_moduleDataByName

NSMutableArray<RCTModuleData *> *_moduleDataByID;

NSMutableArray<Class> *_moduleClassesByID;

部分RCTModuleData模块需要在bridge的_moduleSetupComplete = true之前就进行[moduleData instance] -- (void)[moduleData instance];

标记bridge的_moduleSetupComplete, 标记 bridge的 module setup 完成!!!

异步!!! 大部分的RCTModuleData可以等到bridge完全初始化以后进RCTModuleData instance -- 这类module在实例化时, 会进入调度组dispatchGroup中, 然后异步到mainQueue进行(void)[moduleData instance];

这里有一个重要的标记, RCTCxxBridge.moduleSetupComplete = true, 标记 bridge 的native module setup完成!!!

异步: 大部分的moduleData instance 都会通过dispatchGroup方式, 异步进行[moduleData instance]

4. ModuleClasse包装成RCTModuleData过程

我们知道一个native module被RN bridge使用, 会经历两个过程

module class 成员方法的解析

module instnace过程

先看第一个, 从Class包装成RCTModuleData时, 发生了什么 1. initWithModuleClass 2. setUp: 帮助判断Class中的一些实例方法和大量配置

- (instancetype)initWithModuleClass:(Class)moduleClass moduleProvider:(RCTBridgeModuleProvider)moduleProvider bridge:(RCTBridge *)bridge moduleRegistry:(RCTModuleRegistry *)moduleRegistry viewRegistry_DEPRECATED:(RCTViewRegistry *)viewRegistry_DEPRECATED bundleManager:(RCTBundleManager *)bundleManager callableJSModules:(RCTCallableJSModules *)callableJSModules { if (self = [super init]) { _bridge = bridge; _moduleClass = moduleClass; _moduleProvider = [moduleProvider copy]; _moduleRegistry = moduleRegistry; _viewRegistry_DEPRECATED = viewRegistry_DEPRECATED; _bundleManager = bundleManager; _callableJSModules = callableJSModules; [self setUp]; } return self; } - (void)setUp { // 1. instance 是否实现 -batchDidComplete 方法 _implementsBatchDidComplete = [_moduleClass instancesRespondToSelector:@selector(batchDidComplete)]; // 2. instance 是否实现 -batchDidComplete 方法 _implementsPartialBatchDidFlush = [_moduleClass instancesRespondToSelector:@selector(partialBatchDidFlush)]; // 3. instance 是否是用常量需要暴露出去 _hasConstantsToExport = [_moduleClass instancesRespondToSelector:@selector(constantsToExport)]; // 4. module 是否强制在 main queue 中 setup const BOOL implementsRequireMainQueueSetup = [_moduleClass respondsToSelector:@selector(requiresMainQueueSetup)]; if (implementsRequireMainQueueSetup) { _requiresMainQueueSetup = [_moduleClass requiresMainQueueSetup]; } else { ... // 5. 没实现 requires 时, // - 是否有自定义 -init 初始化方法 // - _hasConstantsToExport || hasCustomInit 时, 需要main queue setup const BOOL hasCustomInit = !_instance && [_moduleClass instanceMethodForSelector:@selector(init)] != objectInitMethod; _requiresMainQueueSetup = _hasConstantsToExport || hasCustomInit; } }

其中比较关键的是通过RCTModuleData描述Module Class的几个关键属性:

_implementsBatchDidComplete - 标记 instance 是否实现关键方法

_implementsPartialBatchDidFlush - 标记 instance 是否实现关键方法

_hasConstantsToExport - 标记 module 是否有 Constants Dictionary 暴露给 JS

_requiresMainQueueSetup - 注意在_prepareModulesWithDispatchGroup中会使用, 标记 module instance setup 是, 是否强制在main queue中调用. RN默认会在子线程中进行module instance setup

5. RCTModuleData在什么时候进行module instance

native module在真正被JS使用之前, 需要对RCTModuleData进行模块实例化 -- module instnace, 也称为module instance setup, 前面提到过, 大量的RCTModuleData的module instnace过程会在RCTCxxBridge._prepareModulesWithDispatchGroup(...)方法中实现:

/// RCTCxxBridge 处理所有的 RCTModuleData, 对它们调用 `moduleData instance` 方法的过程 - (void)_prepareModulesWithDispatchGroup:(dispatch_group_t)dispatchGroup { // 1. 根据是否有 dispatchGroup 决定后续进行 `module instance` 是否异步进行 BOOL initializeImmediately = NO; if (dispatchGroup == NULL) { RCTAssertMainQueue(); initializeImmediately = YES; } // 2. _moduleDataByID 中缓存着所有的`module class`构造的`RCTModuleData`, 遍历它们, 构造一个 block, 根据 initializeImmediately 参数, 决定是否立即执行 for (RCTModuleData *moduleData in _moduleDataByID) { // 3. 注意, 强制 moduleData.requiresMainQueueSetup, 也就是`module instance`强制在main queue 进行`setup` if (moduleData.requiresMainQueueSetup) { // 4. `module instance setup` 的过程, 直接主动调用 [moduleData instance], 并强制触发 `[moduleData gatherConstants]`, 收集 instance 要暴露给 JS 的常量Dictionary dispatch_block_t block = ^{ if (self.valid && ![moduleData.moduleClass isSubclassOfClass:[RCTCxxModule class]]) { (void)[moduleData instance]; if (!RCTIsMainQueueExecutionOfConstantsToExportDisabled()) { [moduleData gatherConstants]; } } }; if (initializeImmediately && RCTIsMainQueue()) { block(); } else { if (dispatchGroup) { dispatch_group_async(dispatchGroup, dispatch_get_main_queue(), block); } } // 5. RCTCxxBridge 中 记录所有在 main queue 中 进行 `module instance` 的module个数 _modulesInitializedOnMainQueue++; } } }

通过上面的代码, 我们能看到: 只有moduleData.requiresMainQueueSetup == true时, 才会在启动阶段进行module instance, 如果没有强制要求在主线程进行module instance setup那么就是 lazy module instance, 或者称为懒实例化的模块!!!

通过前面的RN的代码, 我们能看到, 有以下几种场景, module instance被强制在启动阶段被module instance:

在实现RCTBridgeModule协议时, +requiresMainQueueSetup 方法返回 true

如果没有实现+requiresMainQueueSetup方法, 判断 hasCustomInit || _hasConstantsToExport, 也就是如果有 自定义-init方法, 或者 Constants暴露给JS, 必须强制在 main queue setup

5. RCTModuleData在进行module instance的细节

直接贴上 RCTModuleData的instance方法, 其中最重要的是方法-setUpInstanceAndBridge:

- (id<RCTBridgeModule>)instance { ... // 1. _setupComplete 标记 module instance 是否 setup 完成 if(!_setupComplete) { [self setUpInstanceAndBridge:requestId]; } ... return _instance; } - (void)setUpInstanceAndBridge:(int32_t)requestId { NSString *moduleName = [self name]; // 注意: 使用 instanceLock 保护 _instance 成员 { std::unique_lock<std::mutex> lock(_instanceLock); // 1. 判断 moduleData._setupComplete 是否完成 setup BOOL shouldSetup = !_setupComplete && _bridge.valid; // 2. 如果 moduleData._instance 没实例化, 使用 _moduleProvider() 实例化 // - 如果实例化失败, 也需要标记 _setupComplete = true if (shouldSetup) { if (!_instance) { // 使用 moduleProvider() 调用实例化 _instance = _moduleProvider ? _moduleProvider() : nil; if (!_instance) { _setupComplete = YES; } } } // 3. 将常见属性(bridge, moduleRegistry...)注入到 module instnace 中! if (shouldSetup) { [self setBridgeForInstance]; [self setModuleRegistryForInstance]; [self setViewRegistryForInstance]; [self setBundleManagerForInstance]; [self setCallableJSModulesForInstance]; } // 4. 初始化 module instance 的 methodQueue [self setUpMethodQueue]; // 5. 调用 module instance 中的 initialize 方法, 如果实现了的话 if (shouldSetup) { [self _initializeModule]; } } // instanceLock 释放 // 6. 什么时候通知全局 module instance 完成!!! if (_bridge.moduleSetupComplete) { // 6.1 大部分 module instance 是在 moduleSetupComplete = ture 执行, 会主动通知全局 [self finishSetupForInstance]; } else { // 6.2 少部分在 moduleSetupComplete = false 执行, 标记 _requiresMainQueueSetup = NO, 这样 module instance 实际只setup 一半, lazy instance 使用时, `_bridge.moduleSetupComplete` 一定为 true, 再进行通知, 调用 `finishSetupForInstance`方法 _requiresMainQueueSetup = NO; } } - (void)setUpMethodQueue { if (_instance && !_methodQueue && _bridge.valid) { // 1. instance 是否主动指定 methodQueue, moduleData持有 BOOL implementsMethodQueue = [_instance respondsToSelector:@selector(methodQueue)]; if (implementsMethodQueue && _bridge.valid) { _methodQueue = _instance.methodQueue; } // 2. instance 没有指定, 创建 子线程, 作为 methodQueue if (!_methodQueue && _bridge.valid) { _queueName = [NSString stringWithFormat:@"com.facebook.react.%@Queue", self.name]; _methodQueue = dispatch_queue_create(_queueName.UTF8String, DISPATCH_QUEUE_SERIAL); // 3. 如果`module instance` 主动实现 methodQueue, 使用KVC设置给`module instance` methodQueue!!! if (implementsMethodQueue) { @try { [(id)_instance setValue:_methodQueue forKey:@"methodQueue"]; } @catch (NSException *exception) { RCTLogError(); } } } } // 如果 `module instance` 实现 `RCTBridgeModule`协议的`-initialize`方法, 主动调用, 并标记`_isInitialized`执行过 - (void)_initializeModule { if (!_isInitialized && [_instance respondsToSelector:@selector(initialize)]) { _isInitialized = YES; [(id<RCTInitializing>)_instance initialize]; } } // 如果执行这个方法, 通知bridge + 全局: 这个 module `setupComplete`!!! - (void)finishSetupForInstance { if (!_setupComplete && _instance) { _setupComplete = YES; [_bridge registerModuleForFrameUpdates:_instance withModuleData:self]; [[NSNotificationCenter defaultCenter] postNotificationName:RCTDidInitializeModuleNotification object:_bridge userInfo:@{@"module" : _instance, @"bridge" : RCTNullIfNil(_bridge.parentBridge)}]; } }

在module instance过程中, 有以下几个点需要注意:

instance = _moduleProvider(), 而 _moduleProvider实现基本都是[moduleClass new], 也就是说使用-init进行初始化.

能看给module instance注入大量属性(bridge, ModuelRegistry...)时, 都是使用的 KVC, 并包装在try-catch中, 因为RCTBridgeModule协议实现了接口, 如果 module需要使用这些注入的 API, 需要手动使用@synthesize生成对应的成员变量.

关于method queue, 后续 JS 调用 module instance的export method时, 会异步到method queue中执行.

调用module instance的-initialize是RCTBridgeModule协议提供的, 请与OC中的+initiali...进行区分

关于finishSetupForInstance的调用时机, 请参考代码注释. 另外, 只要它调用会进行如下操作:

标记 _setupComplete = YES

需要通知并更新, bridge中FrameUpdates相关的module

全局通知RCTDidInitializeModuleNotification

以上就是iOS RN启动中管理Native Module详解的详细内容,更多关于iOS RN启动管理Native Module的资料请关注软件开发网其它相关文章!



module native IOS

需要 登录 后方可回复, 如果你还没有账号请 注册新账号