欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

iOS?RN啟動中管理Native?Module詳解

 更新時間:2022年09月07日 09:50:35   作者:brownfeng  
這篇文章主要為大家介紹了iOS?RN啟動中?Native?Module?是如何被管理的,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

1. 全局的 native module 注冊表

RCTModuleClasses 數(shù)組

首先, RN中擁有一個全局的靜態(tài)數(shù)組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 協(xié)議 

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

RN中如果要實現(xiàn)一個native module, 需要實現(xiàn)RCTBridgeModule協(xié)議, 并且RN官方要求我們在實現(xiàn)協(xié)議時, 必須加入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)宏的實現(xiàn)中, 會在+load中調(diào)用RCTRegisterModule(self); 方法, 將Native Module Class注入到全局的靜態(tài)數(shù)組RCTModuleClasses中!!!

也就是說, iOS APP在啟動執(zhí)行main函數(shù)之前, 所有的實現(xiàn)RCTBridgeModule協(xié)議的類型Class, 都會被注入到RCTModuleClasses這個數(shù)組中.

3. RCTModuleClasses中Class數(shù)據(jù)的處理

RCTCxxbridge的start方法執(zhí)行時, RCTGetModuleClasses()方法里面已經(jīng)擁有了所有需要注冊到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 數(shù)組
    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在實例化時, 會進入調(diào)度組dispatchGroup中, 然后異步到mainQueue進行(void)[moduleData instance];

這里有一個重要的標記, RCTCxxBridge.moduleSetupComplete = true, 標記 bridge 的native module setup完成!!!

異步: 大部分的moduleData instance 都會通過dispatchGroup方式, 異步進行[moduleData instance]

4. ModuleClasse包裝成RCTModuleData過程

我們知道一個native module被RN bridge使用, 會經(jīng)歷兩個過程

  • module class 成員方法的解析
  • module instnace過程

先看第一個, 從Class包裝成RCTModuleData時, 發(fā)生了什么 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 是否實現(xiàn) -batchDidComplete 方法
    _implementsBatchDidComplete = [_moduleClass instancesRespondToSelector:@selector(batchDidComplete)];
    // 2. instance 是否實現(xiàn) -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. 沒實現(xiàn) requires 時,
        //  - 是否有自定義 -init 初始化方法
        //  - _hasConstantsToExport || hasCustomInit 時, 需要main queue setup
        const BOOL hasCustomInit = !_instance && [_moduleClass instanceMethodForSelector:@selector(init)] != objectInitMethod;
        _requiresMainQueueSetup = _hasConstantsToExport || hasCustomInit;
    }
}

其中比較關鍵的是通過RCTModuleData描述Module Class的幾個關鍵屬性:

  • _implementsBatchDidComplete - 標記 instance 是否實現(xiàn)關鍵方法
  • _implementsPartialBatchDidFlush - 標記 instance 是否實現(xiàn)關鍵方法
  • _hasConstantsToExport - 標記 module 是否有 Constants Dictionary 暴露給 JS
  • _requiresMainQueueSetup - 注意在_prepareModulesWithDispatchGroup中會使用, 標記 module instance setup 是, 是否強制在main queue中調(diào)用. RN默認會在子線程中進行module instance setup

5. RCTModuleData在什么時候進行module instance

native module在真正被JS使用之前, 需要對RCTModuleData進行模塊實例化 -- module instnace, 也稱為module instance setup, 前面提到過, 大量的RCTModuleData的module instnace過程會在RCTCxxBridge._prepareModulesWithDispatchGroup(...)方法中實現(xiàn):

/// RCTCxxBridge 處理所有的 RCTModuleData, 對它們調(diào)用 `moduleData instance` 方法的過程
- (void)_prepareModulesWithDispatchGroup:(dispatch_group_t)dispatchGroup {
    // 1. 根據(jù)是否有 dispatchGroup 決定后續(xù)進行 `module instance` 是否異步進行
    BOOL initializeImmediately = NO;
    if (dispatchGroup == NULL) {
        RCTAssertMainQueue();
        initializeImmediately = YES;
    }
    // 2. _moduleDataByID 中緩存著所有的`module class`構造的`RCTModuleData`, 遍歷它們, 構造一個 block, 根據(jù) initializeImmediately 參數(shù), 決定是否立即執(zhí)行
    for (RCTModuleData *moduleData in _moduleDataByID) {
        // 3. 注意, 強制 moduleData.requiresMainQueueSetup, 也就是`module instance`強制在main queue 進行`setup`
        if (moduleData.requiresMainQueueSetup) {
            // 4. `module instance setup` 的過程, 直接主動調(diào)用 [moduleData instance], 并強制觸發(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 中 進行 `module instance` 的module個數(shù)
            _modulesInitializedOnMainQueue++;
        }
    }
}

通過上面的代碼, 我們能看到: 只有moduleData.requiresMainQueueSetup == true時, 才會在啟動階段進行module instance, 如果沒有強制要求在主線程進行module instance setup那么就是 lazy module instance, 或者稱為懶實例化的模塊!!!

通過前面的RN的代碼, 我們能看到, 有以下幾種場景, module instance被強制在啟動階段被module instance:

  • 在實現(xiàn)RCTBridgeModule協(xié)議時, +requiresMainQueueSetup 方法返回 true
  • 如果沒有實現(xiàn)+requiresMainQueueSetup方法, 判斷 hasCustomInit || _hasConstantsToExport, 也就是如果有 自定義-init方法, 或者 Constants暴露給JS, 必須強制在 main queue setup

5. RCTModuleData在進行module instance的細節(jié)

直接貼上 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() 調(diào)用實例化
                _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. 調(diào)用 module instance 中的 initialize 方法, 如果實現(xiàn)了的話
        if (shouldSetup) {
            [self _initializeModule];
        }
    } // instanceLock 釋放
    // 6. 什么時候通知全局 module instance 完成!!!
    if (_bridge.moduleSetupComplete) {
        // 6.1 大部分 module instance 是在 moduleSetupComplete = ture 執(zhí)行, 會主動通知全局
        [self finishSetupForInstance];
    } else {  
        // 6.2 少部分在 moduleSetupComplete = false 執(zhí)行, 標記 _requiresMainQueueSetup = NO, 這樣 module instance 實際只setup 一半, lazy instance 使用時, `_bridge.moduleSetupComplete` 一定為 true, 再進行通知, 調(diào)用 `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 沒有指定, 創(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` 主動實現(xiàn) methodQueue, 使用KVC設置給`module instance` methodQueue!!!
            if (implementsMethodQueue) {
                @try {
                    [(id)_instance setValue:_methodQueue forKey:@"methodQueue"];
                } @catch (NSException *exception) {
                    RCTLogError();
                }
            }
      }
}
// 如果 `module instance` 實現(xiàn) `RCTBridgeModule`協(xié)議的`-initialize`方法, 主動調(diào)用, 并標記`_isInitialized`執(zhí)行過
- (void)_initializeModule {
    if (!_isInitialized && [_instance respondsToSelector:@selector(initialize)]) {
        _isInitialized = YES;
        [(id<RCTInitializing>)_instance initialize];
    }
}
// 如果執(zhí)行這個方法, 通知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實現(xiàn)基本都是[moduleClass new], 也就是說使用-init進行初始化.
  • 能看給module instance注入大量屬性(bridge, ModuelRegistry...)時, 都是使用的 KVC, 并包裝在try-catch中, 因為RCTBridgeModule協(xié)議實現(xiàn)了接口, 如果 module需要使用這些注入的 API, 需要手動使用@synthesize生成對應的成員變量.
  • 關于method queue, 后續(xù) JS 調(diào)用 module instance的export method時, 會異步到method queue中執(zhí)行.
  • 調(diào)用module instance的-initialize是RCTBridgeModule協(xié)議提供的, 請與OC中的+initiali...進行區(qū)分

關于finishSetupForInstance的調(diào)用時機, 請參考代碼注釋. 另外, 只要它調(diào)用會進行如下操作:

  • 標記 _setupComplete = YES
  • 需要通知并更新, bridge中FrameUpdates相關的module
  • 全局通知RCTDidInitializeModuleNotification

以上就是iOS RN啟動中管理Native Module詳解的詳細內(nèi)容,更多關于iOS RN啟動管理Native Module的資料請關注腳本之家其它相關文章!

相關文章

  • MAC中顯示隱藏文件和不顯示隱藏文件的方法(超簡單)

    MAC中顯示隱藏文件和不顯示隱藏文件的方法(超簡單)

    下面小編就為大家分享一篇MAC中顯示隱藏文件和不顯示隱藏文件的方法(超簡單),具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-01-01
  • iOS UISegmentControl實現(xiàn)自定義分欄效果

    iOS UISegmentControl實現(xiàn)自定義分欄效果

    這篇文章主要為大家詳細介紹了iOS UISegmentControl實現(xiàn)自定義分欄效果,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • iOS之數(shù)據(jù)解析之XML解析詳解

    iOS之數(shù)據(jù)解析之XML解析詳解

    本篇文章主要介紹了iOS之數(shù)據(jù)解析之XML解析詳解,XML解析常見的兩種方式:DOM解析和SAX解析,有興趣的可以了解一下。
    2016-12-12
  • 詳解iOS App開發(fā)中Cookie的管理方法

    詳解iOS App開發(fā)中Cookie的管理方法

    iOS中主要靠NSHTTPCookieStorage和NSHTTPCookie來管理Cookie,下面我們就來詳解iOS App開發(fā)中Cookie的管理方法,在最后一部分會單獨整理出如何清除Cookie的方法.
    2016-07-07
  • Objective-C之Category實現(xiàn)分類示例詳解

    Objective-C之Category實現(xiàn)分類示例詳解

    這篇文章主要為大家介紹了Objective-C之Category實現(xiàn)分類示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-08-08
  • iOS中UIImagePickerController圖片選取器的用法小結

    iOS中UIImagePickerController圖片選取器的用法小結

    UIImagePickerController平時就是用來做應用中從相冊中選取圖片功能的,這里我們就來整理一下iOS中UIImagePickerController圖片選取器的用法小結,需要的朋友可以參考下
    2016-05-05
  • iOS 開發(fā)常用宏總結

    iOS 開發(fā)常用宏總結

    這篇文章主要介紹了iOS 開發(fā)常用宏總結的相關資料,需要的朋友可以參考下
    2016-09-09
  • iOS實時錄音和播放功能

    iOS實時錄音和播放功能

    這篇文章主要為大家詳細介紹了iOS實時錄音和播放功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-06-06
  • iOS二維碼的生成代碼

    iOS二維碼的生成代碼

    這篇文章主要為大家詳細介紹了iOS二維碼的生成代碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-08-08
  • OC runtime學習筆記之關聯(lián)對象

    OC runtime學習筆記之關聯(lián)對象

    這篇文章主要介紹了OC runtime學習筆記之關聯(lián)對象的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2018-09-09

最新評論