目录
- 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. | |
*/ | |
RCT_EXTERN void RCTRegisterModule(Class); \ | |
+(NSString *)moduleName \ | |
{ \ | |
return @ | |
} \ | |
+(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