当前位置: 代码网 > it编程>App开发>苹果IOS > iOS RN启动中管理Native Module详解

iOS RN启动中管理Native Module详解

2024年05月19日 • 苹果IOS •我要评论
1. 全局的 native module 注册表rctmoduleclasses 数组首先, rn中拥有一个全局的静态数组rctmoduleclasses, 使用它来记录所有的native modul

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的资料请关注代码网其它相关文章!

赞 (0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com