iOS?RN啟動(dòng)中管理Native?Module詳解
1. 全局的 native module 注冊(cè)表
RCTModuleClasses 數(shù)組
首先, RN中擁有一個(gè)全局的靜態(tài)數(shù)組RCTModuleClasses, 使用它來(lái)記錄所有的Native Module的Class, 可以認(rèn)為這是一個(gè)待啟動(dòng)的Native Module配置表!!!
并且提供了一個(gè)全局方法void RCTRegisterModule(Class);, 向這個(gè)RCTModuleClasses注入新的module class:
// 1. 全局注冊(cè)中心, RN 用 RCTModuleClasses 來(lái)持有全局注冊(cè)的 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 寫(xiě)
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 寫(xiě)
});
dispatch_barrier_async(RCTModuleClassesSyncQueue, ^{
[RCTModuleClasses addObject:moduleClass];
});
}
2. RCTBridgeModule 協(xié)議
APP在pre main將module注入全局表中
RN中如果要實(shí)現(xiàn)一個(gè)native module, 需要實(shí)現(xiàn)RCTBridgeModule協(xié)議, 并且RN官方要求我們?cè)趯?shí)現(xiàn)協(xié)議時(shí), 必須加入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)宏的實(shí)現(xiàn)中, 會(huì)在+load中調(diào)用RCTRegisterModule(self); 方法, 將Native Module Class注入到全局的靜態(tài)數(shù)組RCTModuleClasses中!!!
也就是說(shuō), iOS APP在啟動(dòng)執(zhí)行main函數(shù)之前, 所有的實(shí)現(xiàn)RCTBridgeModule協(xié)議的類(lèi)型Class, 都會(huì)被注入到RCTModuleClasses這個(gè)數(shù)組中.
3. RCTModuleClasses中Class數(shù)據(jù)的處理
RCTCxxbridge的start方法執(zhí)行時(shí), RCTGetModuleClasses()方法里面已經(jīng)擁有了所有需要注冊(cè)到bridge的native module配置表, 因此直接可以對(duì)這個(gè)module配置表中, 所有的 class進(jìn)行初始化!!!
//(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 => 包裝成中間管理類(lèi) RCTModuleData
/// 2. 然后將 RCTModuleData 對(duì)象放到 bridge 的幾個(gè)特定容器中管理
NSArray<RCTModuleData *> *moduleDataById = [self _registerModulesForClasses:modules lazilyDiscovered:lazilyDiscovered];
if (lazilyDiscovered) {
...
} else {
// 初始化時(shí)候, 會(huì)走這里
/// 3. 處理 moduleData.hasInstance 的場(chǎng)景, 在 bridge._moduleSetupComplete 之前, module強(qiáng)制進(jìn)行instance實(shí)例化, 并管理在 moduleData 中
for (RCTModuleData *moduleData in _moduleDataByID) {
if (moduleData.hasInstance && (!moduleData.requiresMainQueueSetup || RCTIsMainQueue())) {
(void)[moduleData instance];
}
}
/// 4. 標(biāo)記 bridge 中的 module 完成 setup 工作
_moduleSetupComplete = YES;
/// 5. 對(duì) bridge 中的 module 異步 preapre
[self _prepareModulesWithDispatchGroup:dispatchGroup];
}
/// 6. 返回所有的 moduleData 數(shù)組
return moduleDataById;
}
主要來(lái)說(shuō)是做以下幾個(gè)事情:
- _registerModulesForClasses, 主要工作: Module Class包裝成RCTModuleData:
將每個(gè)moduleClass封裝成一個(gè)個(gè)RCTModuleData 對(duì)象moduleData
然后對(duì)moduleData進(jìn)行setUp()
- 將RCTModuleData注冊(cè)到bridge的module管理的屬性中:
NSMutableDictionary<NSString *, RCTModuleData *> *_moduleDataByName
NSMutableArray<RCTModuleData *> *_moduleDataByID;
NSMutableArray<Class> *_moduleClassesByID;
- 部分RCTModuleData模塊需要在bridge的_moduleSetupComplete = true之前就進(jìn)行[moduleData instance] -- (void)[moduleData instance];
- 標(biāo)記bridge的_moduleSetupComplete, 標(biāo)記 bridge的 module setup 完成!!!
- 異步!!! 大部分的RCTModuleData可以等到bridge完全初始化以后進(jìn)RCTModuleData instance -- 這類(lèi)module在實(shí)例化時(shí), 會(huì)進(jìn)入調(diào)度組dispatchGroup中, 然后異步到mainQueue進(jìn)行(void)[moduleData instance];
這里有一個(gè)重要的標(biāo)記, RCTCxxBridge.moduleSetupComplete = true, 標(biāo)記 bridge 的native module setup完成!!!
異步: 大部分的moduleData instance 都會(huì)通過(guò)dispatchGroup方式, 異步進(jìn)行[moduleData instance]
4. ModuleClasse包裝成RCTModuleData過(guò)程
我們知道一個(gè)native module被RN bridge使用, 會(huì)經(jīng)歷兩個(gè)過(guò)程
- module class 成員方法的解析
- module instnace過(guò)程
先看第一個(gè), 從Class包裝成RCTModuleData時(shí), 發(fā)生了什么 1. initWithModuleClass 2. setUp: 幫助判斷Class中的一些實(shí)例方法和大量配置
- (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 是否實(shí)現(xiàn) -batchDidComplete 方法
_implementsBatchDidComplete = [_moduleClass instancesRespondToSelector:@selector(batchDidComplete)];
// 2. instance 是否實(shí)現(xiàn) -batchDidComplete 方法
_implementsPartialBatchDidFlush = [_moduleClass instancesRespondToSelector:@selector(partialBatchDidFlush)];
// 3. instance 是否是用常量需要暴露出去
_hasConstantsToExport = [_moduleClass instancesRespondToSelector:@selector(constantsToExport)];
// 4. module 是否強(qiáng)制在 main queue 中 setup
const BOOL implementsRequireMainQueueSetup = [_moduleClass respondsToSelector:@selector(requiresMainQueueSetup)];
if (implementsRequireMainQueueSetup) {
_requiresMainQueueSetup = [_moduleClass requiresMainQueueSetup];
} else {
...
// 5. 沒(méi)實(shí)現(xiàn) requires 時(shí),
// - 是否有自定義 -init 初始化方法
// - _hasConstantsToExport || hasCustomInit 時(shí), 需要main queue setup
const BOOL hasCustomInit = !_instance && [_moduleClass instanceMethodForSelector:@selector(init)] != objectInitMethod;
_requiresMainQueueSetup = _hasConstantsToExport || hasCustomInit;
}
}
其中比較關(guān)鍵的是通過(guò)RCTModuleData描述Module Class的幾個(gè)關(guān)鍵屬性:
- _implementsBatchDidComplete - 標(biāo)記 instance 是否實(shí)現(xiàn)關(guān)鍵方法
- _implementsPartialBatchDidFlush - 標(biāo)記 instance 是否實(shí)現(xiàn)關(guān)鍵方法
- _hasConstantsToExport - 標(biāo)記 module 是否有 Constants Dictionary 暴露給 JS
- _requiresMainQueueSetup - 注意在_prepareModulesWithDispatchGroup中會(huì)使用, 標(biāo)記 module instance setup 是, 是否強(qiáng)制在main queue中調(diào)用. RN默認(rèn)會(huì)在子線程中進(jìn)行module instance setup
5. RCTModuleData在什么時(shí)候進(jìn)行module instance
native module在真正被JS使用之前, 需要對(duì)RCTModuleData進(jìn)行模塊實(shí)例化 -- module instnace, 也稱為module instance setup, 前面提到過(guò), 大量的RCTModuleData的module instnace過(guò)程會(huì)在RCTCxxBridge._prepareModulesWithDispatchGroup(...)方法中實(shí)現(xiàn):
/// RCTCxxBridge 處理所有的 RCTModuleData, 對(duì)它們調(diào)用 `moduleData instance` 方法的過(guò)程
- (void)_prepareModulesWithDispatchGroup:(dispatch_group_t)dispatchGroup {
// 1. 根據(jù)是否有 dispatchGroup 決定后續(xù)進(jìn)行 `module instance` 是否異步進(jìn)行
BOOL initializeImmediately = NO;
if (dispatchGroup == NULL) {
RCTAssertMainQueue();
initializeImmediately = YES;
}
// 2. _moduleDataByID 中緩存著所有的`module class`構(gòu)造的`RCTModuleData`, 遍歷它們, 構(gòu)造一個(gè) block, 根據(jù) initializeImmediately 參數(shù), 決定是否立即執(zhí)行
for (RCTModuleData *moduleData in _moduleDataByID) {
// 3. 注意, 強(qiáng)制 moduleData.requiresMainQueueSetup, 也就是`module instance`強(qiáng)制在main queue 進(jìn)行`setup`
if (moduleData.requiresMainQueueSetup) {
// 4. `module instance setup` 的過(guò)程, 直接主動(dòng)調(diào)用 [moduleData instance], 并強(qiáng)制觸發(fā) `[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 中 進(jìn)行 `module instance` 的module個(gè)數(shù)
_modulesInitializedOnMainQueue++;
}
}
}
通過(guò)上面的代碼, 我們能看到: 只有moduleData.requiresMainQueueSetup == true時(shí), 才會(huì)在啟動(dòng)階段進(jìn)行module instance, 如果沒(méi)有強(qiáng)制要求在主線程進(jìn)行module instance setup那么就是 lazy module instance, 或者稱為懶實(shí)例化的模塊!!!
通過(guò)前面的RN的代碼, 我們能看到, 有以下幾種場(chǎng)景, module instance被強(qiáng)制在啟動(dòng)階段被module instance:
- 在實(shí)現(xiàn)RCTBridgeModule協(xié)議時(shí), +requiresMainQueueSetup 方法返回 true
- 如果沒(méi)有實(shí)現(xiàn)+requiresMainQueueSetup方法, 判斷 hasCustomInit || _hasConstantsToExport, 也就是如果有 自定義-init方法, 或者 Constants暴露給JS, 必須強(qiáng)制在 main queue setup
5. RCTModuleData在進(jìn)行module instance的細(xì)節(jié)
直接貼上 RCTModuleData的instance方法, 其中最重要的是方法-setUpInstanceAndBridge:
- (id<RCTBridgeModule>)instance {
...
// 1. _setupComplete 標(biāo)記 module instance 是否 setup 完成
if(!_setupComplete) {
[self setUpInstanceAndBridge:requestId];
}
...
return _instance;
}
- (void)setUpInstanceAndBridge:(int32_t)requestId {
NSString *moduleName = [self name];
// 注意: 使用 instanceLock 保護(hù) _instance 成員
{
std::unique_lock<std::mutex> lock(_instanceLock);
// 1. 判斷 moduleData._setupComplete 是否完成 setup
BOOL shouldSetup = !_setupComplete && _bridge.valid;
// 2. 如果 moduleData._instance 沒(méi)實(shí)例化, 使用 _moduleProvider() 實(shí)例化
// - 如果實(shí)例化失敗, 也需要標(biāo)記 _setupComplete = true
if (shouldSetup) {
if (!_instance) {
// 使用 moduleProvider() 調(diào)用實(shí)例化
_instance = _moduleProvider ? _moduleProvider() : nil;
if (!_instance) {
_setupComplete = YES;
}
}
}
// 3. 將常見(jiàn)屬性(bridge, moduleRegistry...)注入到 module instnace 中!
if (shouldSetup) {
[self setBridgeForInstance];
[self setModuleRegistryForInstance];
[self setViewRegistryForInstance];
[self setBundleManagerForInstance];
[self setCallableJSModulesForInstance];
}
// 4. 初始化 module instance 的 methodQueue
[self setUpMethodQueue];
// 5. 調(diào)用 module instance 中的 initialize 方法, 如果實(shí)現(xiàn)了的話
if (shouldSetup) {
[self _initializeModule];
}
} // instanceLock 釋放
// 6. 什么時(shí)候通知全局 module instance 完成!!!
if (_bridge.moduleSetupComplete) {
// 6.1 大部分 module instance 是在 moduleSetupComplete = ture 執(zhí)行, 會(huì)主動(dòng)通知全局
[self finishSetupForInstance];
} else {
// 6.2 少部分在 moduleSetupComplete = false 執(zhí)行, 標(biāo)記 _requiresMainQueueSetup = NO, 這樣 module instance 實(shí)際只setup 一半, lazy instance 使用時(shí), `_bridge.moduleSetupComplete` 一定為 true, 再進(jìn)行通知, 調(diào)用 `finishSetupForInstance`方法
_requiresMainQueueSetup = NO;
}
}
- (void)setUpMethodQueue {
if (_instance && !_methodQueue && _bridge.valid) {
// 1. instance 是否主動(dòng)指定 methodQueue, moduleData持有
BOOL implementsMethodQueue = [_instance respondsToSelector:@selector(methodQueue)];
if (implementsMethodQueue && _bridge.valid) {
_methodQueue = _instance.methodQueue;
}
// 2. instance 沒(méi)有指定, 創(chuàng)建 子線程, 作為 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` 主動(dòng)實(shí)現(xiàn) methodQueue, 使用KVC設(shè)置給`module instance` methodQueue!!!
if (implementsMethodQueue) {
@try {
[(id)_instance setValue:_methodQueue forKey:@"methodQueue"];
} @catch (NSException *exception) {
RCTLogError();
}
}
}
}
// 如果 `module instance` 實(shí)現(xiàn) `RCTBridgeModule`協(xié)議的`-initialize`方法, 主動(dòng)調(diào)用, 并標(biāo)記`_isInitialized`執(zhí)行過(guò)
- (void)_initializeModule {
if (!_isInitialized && [_instance respondsToSelector:@selector(initialize)]) {
_isInitialized = YES;
[(id<RCTInitializing>)_instance initialize];
}
}
// 如果執(zhí)行這個(gè)方法, 通知bridge + 全局: 這個(gè) 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過(guò)程中, 有以下幾個(gè)點(diǎn)需要注意:
- instance = _moduleProvider(), 而 _moduleProvider實(shí)現(xiàn)基本都是[moduleClass new], 也就是說(shuō)使用-init進(jìn)行初始化.
- 能看給module instance注入大量屬性(bridge, ModuelRegistry...)時(shí), 都是使用的 KVC, 并包裝在try-catch中, 因?yàn)镽CTBridgeModule協(xié)議實(shí)現(xiàn)了接口, 如果 module需要使用這些注入的 API, 需要手動(dòng)使用@synthesize生成對(duì)應(yīng)的成員變量.
- 關(guān)于method queue, 后續(xù) JS 調(diào)用 module instance的export method時(shí), 會(huì)異步到method queue中執(zhí)行.
- 調(diào)用module instance的-initialize是RCTBridgeModule協(xié)議提供的, 請(qǐng)與OC中的+initiali...進(jìn)行區(qū)分
關(guān)于finishSetupForInstance的調(diào)用時(shí)機(jī), 請(qǐng)參考代碼注釋. 另外, 只要它調(diào)用會(huì)進(jìn)行如下操作:
- 標(biāo)記 _setupComplete = YES
- 需要通知并更新, bridge中FrameUpdates相關(guān)的module
- 全局通知RCTDidInitializeModuleNotification
以上就是iOS RN啟動(dòng)中管理Native Module詳解的詳細(xì)內(nèi)容,更多關(guān)于iOS RN啟動(dòng)管理Native Module的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
MAC中顯示隱藏文件和不顯示隱藏文件的方法(超簡(jiǎn)單)
下面小編就為大家分享一篇MAC中顯示隱藏文件和不顯示隱藏文件的方法(超簡(jiǎn)單),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-01-01
iOS UISegmentControl實(shí)現(xiàn)自定義分欄效果
這篇文章主要為大家詳細(xì)介紹了iOS UISegmentControl實(shí)現(xiàn)自定義分欄效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03
iOS之?dāng)?shù)據(jù)解析之XML解析詳解
本篇文章主要介紹了iOS之?dāng)?shù)據(jù)解析之XML解析詳解,XML解析常見(jiàn)的兩種方式:DOM解析和SAX解析,有興趣的可以了解一下。2016-12-12
詳解iOS App開(kāi)發(fā)中Cookie的管理方法
iOS中主要靠NSHTTPCookieStorage和NSHTTPCookie來(lái)管理Cookie,下面我們就來(lái)詳解iOS App開(kāi)發(fā)中Cookie的管理方法,在最后一部分會(huì)單獨(dú)整理出如何清除Cookie的方法.2016-07-07
Objective-C之Category實(shí)現(xiàn)分類(lèi)示例詳解
這篇文章主要為大家介紹了Objective-C之Category實(shí)現(xiàn)分類(lèi)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08
iOS中UIImagePickerController圖片選取器的用法小結(jié)
UIImagePickerController平時(shí)就是用來(lái)做應(yīng)用中從相冊(cè)中選取圖片功能的,這里我們就來(lái)整理一下iOS中UIImagePickerController圖片選取器的用法小結(jié),需要的朋友可以參考下2016-05-05
OC runtime學(xué)習(xí)筆記之關(guān)聯(lián)對(duì)象
這篇文章主要介紹了OC runtime學(xué)習(xí)筆記之關(guān)聯(lián)對(duì)象的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-09-09

