ReactNative新架构之iOS端TurboModule源码剖析
ReactNative新架构之iOS端TurboModule源码剖析
前言
注意,本文是基于React Native 0.83版本源码进行分析。
前面已经通过《ReactNative新架构之Android端TurboModule机制完全解析》了解了很多TurboModule的关键信息,接下来iOS端的分析将会略过一些已知内容,聚焦重点。
初始化
先回顾一下《React Native新架构之iOS端初始化源码分析》一文我们提过的TurboModule初始化流程:
在AppDelegate中有:
// 1: 创建 ReactNativeDelegate
let delegate = ReactNativeDelegate()
// 2: 创建 RCTReactNativeFactory
let factory = RCTReactNativeFactory(delegate: delegate)
// 3: 配置依赖提供者(Codegen 生成的模块)
delegate.dependencyProvider = RCTAppDependencyProvider()
接下来再看看RCTReactNativeFactory 中对RCTTurboModuleManagerDelegate协议的实现:
- (Class)getModuleClassFromName:(const char *)name
{
#if RN_DISABLE_OSS_PLUGIN_HEADER
return RCTTurboModulePluginClassProvider(name);
#else
// 先尝试从 delegate 获取
if ([_delegate respondsToSelector:@selector(getModuleClassFromName:)]) {
Class moduleClass = [_delegate getModuleClassFromName:name];
if (moduleClass != nil) {
return moduleClass;
}
}
return RCTCoreModulesClassProvider(name);
#endif
}
- (nullable id<RCTModuleProvider>)getModuleProvider:(const char *)name
{
if ([_delegate respondsToSelector:@selector(getModuleProvider:)]) {
return [_delegate getModuleProvider:name];
}
return nil;
}
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name
jsInvoker:(std::shared_ptr<facebook::react::CallInvoker>)jsInvoker
{
if ([_delegate respondsToSelector:@selector(getTurboModule:jsInvoker:)]) {
return [_delegate getTurboModule:name jsInvoker:jsInvoker];
}
return facebook::react::DefaultTurboModules::getTurboModule(name, jsInvoker);
}
- (id<RCTTurboModule>)getModuleInstanceFromClass:(Class)moduleClass
{
#if USE_OSS_CODEGEN
if (self.delegate.dependencyProvider == nil) {
[NSException raise:@"ReactNativeFactoryDelegate dependencyProvider is nil"
format:@"Delegate must provide a valid dependencyProvider"];
}
#endif
if ([_delegate respondsToSelector:@selector(getModuleInstanceFromClass:)]) {
id<RCTTurboModule> moduleInstance = [_delegate getModuleInstanceFromClass:moduleClass];
if (moduleInstance != nil) {
return moduleInstance;
}
}
// 使用默认方式创建(通过 dependencyProvider)
return RCTAppSetupDefaultModuleFromClass(moduleClass, self.delegate.dependencyProvider);
}
可以看到,这里面优先调用_delegate中的方法,而我们在自定义的ReactNativeDelegate中并未实现这些方法,那么只能去父类找,也就是RCTDefaultReactNativeFactoryDelegate.mm:
@implementation RCTDefaultReactNativeFactoryDelegate
@synthesize dependencyProvider;
// 省略部分代码......
- (nullable id<RCTModuleProvider>)getModuleProvider:(const char *)name
{
NSString *providerName = [NSString stringWithCString:name encoding:NSUTF8StringEncoding];
return (self.dependencyProvider != nullptr) ? self.dependencyProvider.moduleProviders[providerName] : nullptr;
}
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name
jsInvoker:(std::shared_ptr<facebook::react::CallInvoker>)jsInvoker
{
return facebook::react::DefaultTurboModules::getTurboModule(name, jsInvoker);
}
- (Class)getModuleClassFromName:(const char *)name
{
return nullptr;
}
- (id<RCTTurboModule>)getModuleInstanceFromClass:(Class)moduleClass
{
return nullptr;
}
@end
可以看到,getModuleClassFromName和getModuleInstanceFromClass并没有具体实现。而getModuleProvider方法,也依赖其属性dependencyProvider。这里的dependencyProvider是由外部赋值,也就是初始化时创建的RCTAppDependencyProvider对象。
这里的RCTAppDependencyProvider实际上是由codegen工具自动生成的,并不在代码中。
TurboModule是懒加载的,这里主要是设置了Provider。继续回顾一下RCTInstance.mm中的_start方法:
// 创建 TurboModuleManager
_turboModuleManager = [[RCTTurboModuleManager alloc] initWithBridgeProxy:bridgeProxy
bridgeModuleDecorator:_bridgeModuleDecorator
delegate:self
jsInvoker:jsCallInvoker
devMenuConfigurationDecorator:_devMenuConfigurationDecorator];
// 省略......
// 创建 ContextContainer
auto contextContainer = std::make_shared<ContextContainer>();
[_delegate didCreateContextContainer:contextContainer];
// 插入核心模块
contextContainer->insert(
"RCTImageLoader", facebook::react::wrapManagedObject([_turboModuleManager moduleForName:"RCTImageLoader"]));
contextContainer->insert(
"RCTEventDispatcher",
facebook::react::wrapManagedObject([_turboModuleManager moduleForName:"RCTEventDispatcher"]));
// 初始化 JS Runtime
_reactInstance->initializeRuntime(options, [=](jsi::Runtime &runtime) {
__strong __typeof(self) strongSelf = weakSelf;
if (!strongSelf) {
return;
}
// 安装 TurboModule JS 绑定
[strongSelf->_turboModuleManager installJSBindings:runtime];
// 绑定 Native Logger
facebook::react::bindNativeLogger(runtime, [](const std::string &message, unsigned int logLevel) {
_RCTLogJavaScriptInternal(static_cast<RCTLogLevel>(logLevel), [NSString stringWithUTF8String:message.c_str()]);
});
// 安装 Native Component Registry 绑定
RCTInstallNativeComponentRegistryBinding(runtime);
// 省略......
});
继续跟踪RCTTurboModuleManager的初始化,源码react-native/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModuleManager.mm:
- (instancetype)initWithBridgeProxy:(RCTBridgeProxy *)bridgeProxy
bridgeModuleDecorator:(RCTBridgeModuleDecorator *)bridgeModuleDecorator
delegate:(id<RCTTurboModuleManagerDelegate>)delegate
jsInvoker:(std::shared_ptr<CallInvoker>)jsInvoker
devMenuConfigurationDecorator:(RCTDevMenuConfigurationDecorator *)devMenuConfigurationDecorator
{
return [self initWithBridge:nil
bridgeProxy:bridgeProxy
bridgeModuleDecorator:bridgeModuleDecorator
delegate:delegate
jsInvoker:jsInvoker
devMenuConfigurationDecorator:devMenuConfigurationDecorator];
}
- (instancetype)initWithBridge:(RCTBridge *)bridge
bridgeProxy:(RCTBridgeProxy *)bridgeProxy
bridgeModuleDecorator:(RCTBridgeModuleDecorator *)bridgeModuleDecorator
delegate:(id<RCTTurboModuleManagerDelegate>)delegate
jsInvoker:(std::shared_ptr<CallInvoker>)jsInvoker
devMenuConfigurationDecorator:(RCTDevMenuConfigurationDecorator *)devMenuConfigurationDecorator
{
if (self = [super init]) {
_jsInvoker = std::move(jsInvoker);
_delegate = delegate;
_bridge = bridge;
_bridgeProxy = bridgeProxy;
_bridgeModuleDecorator = bridgeModuleDecorator;
_invalidating = false;
// 创建串行队列 _sharedModuleQueue
_sharedModuleQueue = dispatch_queue_create("com.meta.react.turbomodulemanager.queue", DISPATCH_QUEUE_SERIAL);
_devMenuConfigurationDecorator = devMenuConfigurationDecorator;
if (RCTTurboModuleInteropEnabled()) {
// 省略旧架构兼容处理......
}
// 监听 RCTBridgeWillInvalidateModulesNotification 和 RCTBridgeDidInvalidateModulesNotification 通知,用于处理模块失效场景
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(bridgeWillInvalidateModules:)
name:RCTBridgeWillInvalidateModulesNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(bridgeDidInvalidateModules:)
name:RCTBridgeDidInvalidateModulesNotification
object:nil];
}
return self;
}
RCTTurboModuleManager初始化逻辑非常简单,我们继续查看[strongSelf->_turboModuleManager installJSBindings:runtime]方法调用,它是在JS Runtime初始化完成之后才回调的,现在跟踪一下具体实现,源码react-native/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModuleManager.mm:
- (void)installJSBindings:(facebook::jsi::Runtime &)runtime
{
/**
* 我们会一直保留 TurboModuleManager 实例,直到 JavaScript 虚拟机被销毁。
* 仅从 JavaScript 端使用/创建 TurboModule 是完全有效的。
* 在这种情况下,如果 Objective-C 代码中没有对 TurboModuleManager 的强引用,我们就不应该释放它。
* 因此,我们让 __turboModuleProxy 持有对 TurboModuleManager 的强引用。
*/
auto turboModuleProvider = [self,
runtime = &runtime](const std::string &name) -> std::shared_ptr<react::TurboModule> {
auto moduleName = name.c_str();
TurboModulePerfLogger::moduleJSRequireBeginningStart(moduleName);
// 检查模块是否已初始化
auto moduleWasNotInitialized = ![self moduleIsInitialized:moduleName];
if (moduleWasNotInitialized) {
[self->_bridge.performanceLogger markStartForTag:RCTPLTurboModuleSetup];
}
/**
* 默认情况下,所有 TurboModule 都是长期存在的。
* 此外,如果找不到名称为 `name` 的 TurboModule,则会触发断言失败。
*/
auto turboModule = [self provideTurboModule:moduleName runtime:runtime];
if (moduleWasNotInitialized && [self moduleIsInitialized:moduleName]) {
[self->_bridge.performanceLogger markStopForTag:RCTPLTurboModuleSetup];
}
if (turboModule) {
TurboModulePerfLogger::moduleJSRequireEndingEnd(moduleName);
} else {
TurboModulePerfLogger::moduleJSRequireEndingFail(moduleName);
}
return turboModule;
};
if (RCTTurboModuleInteropEnabled()) {
// 省略旧架构兼容代码......
} else {
TurboModuleBinding::install(runtime, std::move(turboModuleProvider));
}
}
该方法是负责将 TurboModule 系统绑定到 JavaScript 运行时,建立 JS 与原生模块之间的桥梁。
这里的TurboModuleBinding::install方法我们在Android端时已经分析过了,不再赘述。TurboModulePerfLogger是性能日志相关的东西,直接忽略。
所以整个iOS的初始化流程,相比Android端简单太多了。
调用流程
在《ReactNative新架构之Android端TurboModule机制完全解析》一文中,我们已经详细分析过了从JS层到C++层的查找逻辑。现在回顾一下关键地方:
// TurboModuleBinding.cpp
jsi::Value TurboModuleBinding::getModule(
jsi::Runtime& runtime,
const std::string& moduleName) const {
std::shared_ptr<TurboModule> module;
{
TraceSection s("TurboModuleBinding::moduleProvider", "module", moduleName);
module = moduleProvider_(moduleName); // ← 调用moduleProvider获取模块
}
if (module) {
TurboModuleWithJSIBindings::installJSIBindings(module, runtime);
// 省略......
// 状态:在 TurboModule 上未找到 jsRepresentation
// 创建一个全新的 jsRepresentation,并将其附加到 TurboModule
jsi::Object jsRepresentation(runtime);
weakJsRepresentation =
std::make_unique<jsi::WeakObject>(runtime, jsRepresentation);
// 在属性访问时延迟填充 jsRepresentation。
//
// 这是如何工作的?
// 1. 最初 jsRepresentation 是空的:{}
// 2. 如果在 jsRepresentation 上的属性查找失败,JS 运行时将
// 搜索 jsRepresentation 的原型:jsi::Object(TurboModule)。
// 3. TurboModule::get(runtime, propKey) 执行。这会创建
// 属性,将其缓存在 jsRepresentation 上,然后将其返回给JavaScript
auto hostObject =
jsi::Object::createFromHostObject(runtime, std::move(module));
jsRepresentation.setProperty(runtime, "__proto__", std::move(hostObject));
return jsRepresentation;
} else {
return jsi::Value::null();
}
}
这里是通过moduleProvider_方法返回TurboModule实例。而moduleProvider_实际上就是TurboModuleBinding::install方法调用时设置的那个provider闭包。也就是上面分析的installJSBindings中的turboModuleProvider。回顾一下其实现:
auto turboModule = [self provideTurboModule:moduleName runtime:runtime];
闭包中是调用provideTurboModule返回turboModule,跟踪一下实现:
// RCTTurboModuleManager.mm
/**
* 给定一个 TurboModule 的名称,返回一个 C++ 对象,该对象是
* TurboModule C++ 类的实例。这个类包装了 TurboModule 的 ObjC 实例。
* 如果不存在提供名称的 TurboModule ObjC 类,程序将中止。
*
* 注意:所有 TurboModule 实例都被缓存,这意味着它们都是长生命周期的(目前如此)
*/
- (std::shared_ptr<TurboModule>)provideTurboModule:(const char *)moduleName runtime:(jsi::Runtime *)runtime
{
// 缓存查找
auto turboModuleLookup = _turboModuleCache.find(moduleName);
if (turboModuleLookup != _turboModuleCache.end()) {
TurboModulePerfLogger::moduleJSRequireBeginningCacheHit(moduleName);
TurboModulePerfLogger::moduleJSRequireBeginningEnd(moduleName);
return turboModuleLookup->second;
}
TurboModulePerfLogger::moduleJSRequireBeginningEnd(moduleName);
/**
* 步骤 1:查找纯 C++ 模块。
* 纯 C++ 模块具有优先级。
*/
if ([_delegate respondsToSelector:@selector(getTurboModule:jsInvoker:)]) {
int32_t moduleId = getUniqueId();
TurboModulePerfLogger::moduleCreateStart(moduleName, moduleId);
// 尝试从代理获取纯 C++ TurboModule
auto turboModule = [_delegate getTurboModule:moduleName jsInvoker:_jsInvoker];
if (turboModule != nullptr) {
_turboModuleCache.insert({moduleName, turboModule});
TurboModulePerfLogger::moduleCreateEnd(moduleName, moduleId);
return turboModule;
}
TurboModulePerfLogger::moduleCreateFail(moduleName, moduleId);
}
// 在全局导出的 C++ TurboModule 映射中查找
auto &cxxTurboModuleMapProvider = globalExportedCxxTurboModuleMap();
auto it = cxxTurboModuleMapProvider.find(moduleName);
if (it != cxxTurboModuleMapProvider.end()) {
auto turboModule = it->second(_jsInvoker);
_turboModuleCache.insert({moduleName, turboModule});
return turboModule;
}
/**
* 步骤 2:查找平台特定模块
*/
id<RCTModuleProvider> module = [self _moduleProviderForName:moduleName];
TurboModulePerfLogger::moduleJSRequireEndingStart(moduleName);
// 如果我们请求创建一个 TurboModule,其相应的 ObjC 类必须存在
// 如果类不存在,那么 _provideObjCModule 返回 nil
if (!module) {
return nullptr;
}
std::shared_ptr<NativeMethodCallInvoker> nativeMethodCallInvoker = nullptr;
dispatch_queue_t methodQueue = (dispatch_queue_t)objc_getAssociatedObject(module, &kAssociatedMethodQueueKey);
if (methodQueue) {
/**
* 步骤 2c:从 TurboModule 的方法队列创建原生 CallInvoker。
*/
nativeMethodCallInvoker = std::make_shared<ModuleNativeMethodCallInvoker>(methodQueue);
/**
* 让 RCTCxxBridge 装饰原生 CallInvoker,使其能够感知 TurboModule 异步方法调用。
* 这有助于桥接器及时触发 onBatchComplete。
*/
if ([_bridge respondsToSelector:@selector(decorateNativeMethodCallInvoker:)]) {
nativeMethodCallInvoker = [_bridge decorateNativeMethodCallInvoker:nativeMethodCallInvoker];
}
}
该方法是多级查找策略。查找优先级:
- 缓存查找 (最高优先级)
- 代理提供的纯 C++ 模块
- 全局 C++ 模块映射
- 平台特定 ObjC 模块 (最低优先级)
我们先跟踪一下纯C++ TurboModule的查找流程,这里的代理查找,其实最终就是调用DefaultTurboModules::getTurboModule(name, jsInvoker),这是前面已经得到的结论。来看一下其实现,源码react-native/packages/react-native/ReactCommon/react/nativemodule/defaults/DefaultTurboModules.cpp:
/* static */ std::shared_ptr<TurboModule> DefaultTurboModules::getTurboModule(
const std::string& name,
const std::shared_ptr<CallInvoker>& jsInvoker) {
if (name == NativeReactNativeFeatureFlags::kModuleName) {
return std::make_shared<NativeReactNativeFeatureFlags>(jsInvoker);
}
if (name == NativeMicrotasks::kModuleName) {
return std::make_shared<NativeMicrotasks>(jsInvoker);
}
if (name == NativeIdleCallbacks::kModuleName) {
return std::make_shared<NativeIdleCallbacks>(jsInvoker);
}
if (name == NativeDOM::kModuleName) {
return std::make_shared<NativeDOM>(jsInvoker);
}
if (ReactNativeFeatureFlags::enableWebPerformanceAPIsByDefault()) {
if (name == NativePerformance::kModuleName) {
return std::make_shared<NativePerformance>(jsInvoker);
}
}
if (ReactNativeFeatureFlags::enableIntersectionObserverByDefault()) {
if (name == NativeIntersectionObserver::kModuleName) {
return std::make_shared<NativeIntersectionObserver>(jsInvoker);
}
}
#ifdef REACT_NATIVE_DEBUGGER_ENABLED_DEVONLY
if (name == DevToolsRuntimeSettingsModule::kModuleName) {
return std::make_shared<DevToolsRuntimeSettingsModule>(jsInvoker);
}
#endif
return nullptr;
}
可见,此处都是RN内置的一些模块。第三方要实现纯C++ TurboModule,那么注册集成就得考虑从globalExportedCxxTurboModuleMap中下手了。
现在继续查看_moduleProviderForName方法实现:
- (id<RCTModuleProvider>)_moduleProviderForName:(const char *)moduleName
{
id<RCTModuleProvider> moduleProvider = nil;
// 从代理获取模块提供者
if ([_delegate respondsToSelector:@selector(getModuleProvider:)]) {
moduleProvider = [_delegate getModuleProvider:moduleName];
}
if (RCTTurboModuleInteropEnabled() && ![self _isTurboModule:moduleName] && !moduleProvider) {
return nil;
}
if (moduleProvider) {
if ([moduleProvider conformsToProtocol:@protocol(RCTTurboModule)]) {
// 如果模块提供者符合 RCTTurboModule 协议,需要初始化 ObjC 属性(如调度队列)
return (id<RCTModuleProvider>)[self _provideObjCModule:moduleName moduleProvider:moduleProvider];
}
// 否则直接返回(C++ 模块)
return moduleProvider;
}
// 如果没有找到模块提供者,尝试创建通过传统方式注册的 ObjC 模块
return (id<RCTModuleProvider>)[self _provideObjCModule:moduleName moduleProvider:nil];
}
/**
* 给定一个 NativeModule 的名称,返回该 NativeModule Objective-C 类的实例对象。如果不存在名称与给定名称匹配的 NativeModule,则返回 nil。
*
* 注意:所有 NativeModule 实例都会被缓存,这意味着它们都是长期存在的(目前是这样)。
*/
- (id<RCTBridgeModule>)_provideObjCModule:(const char *)moduleName moduleProvider:(id<RCTModuleProvider>)moduleProvider
{
if (strncmp("RCT", moduleName, 3) == 0) {
moduleName = [[[NSString stringWithUTF8String:moduleName] substringFromIndex:3] UTF8String];
}
ModuleHolder *moduleHolder = [self _getOrCreateModuleHolder:moduleName];
if (!moduleHolder) {
return nil;
}
TurboModulePerfLogger::moduleCreateStart(moduleName, moduleHolder->getModuleId());
id<RCTBridgeModule> module = [self _provideObjCModule:moduleName
moduleHolder:moduleHolder
shouldPerfLog:YES
moduleProvider:moduleProvider];
if (module) {
TurboModulePerfLogger::moduleCreateEnd(moduleName, moduleHolder->getModuleId());
} else {
TurboModulePerfLogger::moduleCreateFail(moduleName, moduleHolder->getModuleId());
}
return module;
}
// 获取或者创建一个ModuleHolder
- (ModuleHolder *)_getOrCreateModuleHolder:(const char *)moduleName
{
std::lock_guard<std::mutex> guard(_moduleHoldersMutex);
if (_invalidating) {
return nullptr;
}
return &_moduleHolders[moduleName];
}
// 创建和管理 Objective-C 原生模块的实例,确保线程安全和单例模式
- (id<RCTBridgeModule>)_provideObjCModule:(const char *)moduleName
moduleHolder:(ModuleHolder *)moduleHolder
shouldPerfLog:(BOOL)shouldPerfLog
moduleProvider:(id<RCTModuleProvider>)moduleProvider
{
bool shouldCreateModule = false;
{
std::lock_guard<std::mutex> guard(moduleHolder->mutex());
// 如果模块已创建完成,直接返回缓存的实例
if (moduleHolder->isDoneCreatingModule()) {
if (shouldPerfLog) {
TurboModulePerfLogger::moduleCreateCacheHit(moduleName, moduleHolder->getModuleId());
}
return moduleHolder->getModule();
}
if (!moduleHolder->isCreatingModule()) {
shouldCreateModule = true;
moduleHolder->startCreatingModule();
}
}
if (shouldCreateModule) {
/**
* 步骤 2a:解析特定于平台的类
*/
Class moduleClass = moduleProvider ? [moduleProvider class] : [self _getModuleClassFromName:moduleName];
__block id<RCTBridgeModule> module = nil;
// 判断是否应该创建模块(检查是否实现了RCTTurboModule协议)
if ([self _shouldCreateObjCModule:moduleClass]) {
__weak __typeof(self) weakSelf = self;
dispatch_block_t work = ^{
auto strongSelf = weakSelf;
if (!strongSelf) {
return;
}
module = [strongSelf _createAndSetUpObjCModule:moduleClass moduleName:moduleName moduleId:moduleHolder
->getModuleId()];
};
// 某些模块需要在主线程初始化
if ([self _requiresMainQueueSetup:moduleClass]) {
RCTUnsafeExecuteOnMainQueueSync(work);
} else {
work();
}
}
{
std::lock_guard<std::mutex> guard(moduleHolder->mutex());
moduleHolder->setModule(module);
moduleHolder->endCreatingModule();
}
moduleHolder->cv().notify_all();
return module;
}
std::unique_lock<std::mutex> guard(moduleHolder->mutex());
// 如果其他线程正在创建模块,当前线程等待
while (moduleHolder->isCreatingModule()) {
/**
* TODO(T65905574):
* 如果负责创建和初始化 NativeModule 的线程发生阻塞,我们将在此处无限期等待
* 这是传统 NativeModule 的行为。现在更改此行为可能会导致 TurboModule 中出现比 NativeModule 更多的崩溃/问题
* 从而使 TurboModule 基础设施的测试更加困难。因此,我们应该考虑在 TurboModule 全面推广之后再进行更改
*/
moduleHolder->cv().wait(guard);
}
return moduleHolder->getModule();
}
接下继续跟踪_createAndSetUpObjCModule实现:
/**
* 给定一个 NativeModule 类及其名称,同步创建并初始化它。
*
* 此方法可以从两个不同的上下文中同步调用:
* - 调用 _provideObjCModule: 的线程
* - 主线程(如果 NativeModule 需要主队列初始化),阻塞调用 _provideObjCModule: 的线程。
*/
- (id<RCTBridgeModule>)_createAndSetUpObjCModule:(Class)moduleClass
moduleName:(const char *)moduleName
moduleId:(int32_t)moduleId
{
id<RCTBridgeModule> module = nil;
/**
* 步骤 2b:请求宿主应用/代理来实例化此类
*/
module = [self _getModuleInstanceFromClass:moduleClass];
TurboModulePerfLogger::moduleCreateSetUpStart(moduleName, moduleId);
/**
* NativeModules 不需要/不想要 bridge 是合理的。
* 在这种情况下,它们的实现中不会有 `@synthesize bridge = _bridge`,
* ObjC 运行时也不会生成 `- (RCTBridge *) bridge { ... }` 方法。
* 该属性也不会有 ivar 支持,这使得写入它变得不安全。
* 因此,我们检查此方法是否存在,以确定是否可以安全地将 bridge 设置到 NativeModule。
*/
if ([module respondsToSelector:@selector(bridge)] && (_bridge || _bridgeProxy)) {
/**
* 仅仅因为 NativeModule 有 `bridge` 方法,并不意味着
* 它在其实现中合成了 bridge。因此,
* 我们需要将设置 bridge 到 NativeModule 的代码
* 包裹在 try/catch 中。这可以捕获 NativeModule
* 作者手动指定 `bridge` 方法的情况。
*/
@try {
/**
* RCTBridgeModule 将 bridge 属性声明为只读。
* 因此,当 NativeModules 的作者通过 @synthesize bridge = bridge; 合成 bridge 时,
* ObjC 运行时只生成 - (RCTBridge *) bridge: { ... } 方法。
* 没有生成 setter,所以我们必须依赖 ObjC 的 KVC API
* 来设置这些 NativeModules 的 bridge 属性。
*/
if (_bridge) {
[(id)module setValue:_bridge forKey:@"bridge"];
} else if (_bridgeProxy) {
[(id)module setValue:_bridgeProxy forKey:@"bridge"];
}
} @catch (NSException *exception) {
RCTLogError(
@"%@ has no setter or ivar for its bridge, which is not "
"permitted. You must either @synthesize the bridge property, "
"or provide your own setter method.",
RCTBridgeModuleNameForClass([module class]));
}
}
// 这是 conformsToProtocol:@protocol(RCTCallInvokerModule) 的更高性能替代方案
if ([module respondsToSelector:@selector(setCallInvoker:)]) {
RCTCallInvoker *callInvoker = [[RCTCallInvoker alloc] initWithCallInvoker:_jsInvoker];
[(id<RCTCallInvokerModule>)module setCallInvoker:callInvoker];
}
/**
* 一些模块需要自己的队列,但没有提供,所以我们需要为它们创建。
* 这些模块通常有以下内容:
* `@synthesize methodQueue = _methodQueue`
*/
dispatch_queue_t methodQueue = nil;
BOOL moduleHasMethodQueueGetter = [module respondsToSelector:@selector(methodQueue)];
if (moduleHasMethodQueueGetter) {
methodQueue = [(id<RCTBridgeModule>)module methodQueue];
}
/**
* 注意:RCTJSThread 是一个有效的方法队列,定义为 (id)kCFNull。
* 它应该正确地不进入以下 if 条件块。
*/
if (!methodQueue) {
methodQueue = _sharedModuleQueue;
if (moduleHasMethodQueueGetter) {
/**
* 如果模块有方法队列 getter,有两种情况:
* - 我们 @synthesize 了方法队列。在这种情况下,getter 最初会返回 nil。
* - 我们在 NativeModule 上有自定义的 methodQueue 函数。如果我们走到这里,
* 那么该 getter 返回了 nil。
*
* 因此,我们使用 try/catch 和 ObjC 的 KVC API 尝试将方法队列分配给 NativeModule。
* 在情况 1 中,我们会成功。在情况 2 中,会抛出异常,我们将忽略它。
*/
@try {
[(id)module setValue:methodQueue forKey:@"methodQueue"];
} @catch (NSException *exception) {
RCTLogError(
@"%@ has no setter or ivar for its methodQueue, which is not "
"permitted. You must either @synthesize the methodQueue property, "
"or provide your own setter method.",
RCTBridgeModuleNameForClass([module class]));
}
}
}
/**
* 用 bridgeless 兼容的 API 装饰 NativeModules,这些 API 会调用 bridge。
*/
if (_bridgeModuleDecorator) {
[_bridgeModuleDecorator attachInteropAPIsToModule:module];
}
/**
* 如果 NativeModule 符合 RCTInitializing 协议,调用其 initialize 方法
*/
if ([module respondsToSelector:@selector(initialize)]) {
[(id<RCTInitializing>)module initialize];
}
#if RCT_DEV_MENU
[_devMenuConfigurationDecorator decorate:module];
#endif
/**
* 将方法队列附加到 id<RCTBridgeModule> 对象。
* 这是必要的,因为 id<RCTBridgeModule> 对象可以在需要方法队列之前被急切地创建/初始化。
* 方法队列用于 id<RCTBridgeModule> 的 JS -> Native 调用。
* 因此,我们需要在 provideTurboModule:runtime: 中创建 id<RCTBridgeModule> 的
* TurboModule jsi::HostObject 之前拥有它。
*/
objc_setAssociatedObject(module, &kAssociatedMethodQueueKey, methodQueue, OBJC_ASSOCIATION_RETAIN);
/**
* 广播此 NativeModule 已创建
*
* TODO(T41180176): Investigate whether we can delete this after TM
* rollout.
*/
[[NSNotificationCenter defaultCenter]
postNotificationName:RCTDidInitializeModuleNotification
object:_bridge
userInfo:@{@"module" : module, @"bridge" : RCTNullIfNil([_bridge parentBridge])}];
TurboModulePerfLogger::moduleCreateSetUpEnd(moduleName, moduleId);
return module;
}
这个方法实现很长,但其实就是创建 ObjC 模块实例并注入所有必要的依赖。具体说,就是:
-
注入 BridgeProxy,让模块能调用 JS
-
注入 CallInvoker,新架构的 JSI 调用机制
-
配置方法队列,模块的异步方法在此队列执行
继续查看_getModuleInstanceFromClass方法的实现:
- (id<RCTBridgeModule>)_getModuleInstanceFromClass:(Class)moduleClass
{
NSString *moduleNameStr = RCTBridgeModuleNameForClass(moduleClass);
// 省略旧架构代码......
id<RCTBridgeModule> module = (id<RCTBridgeModule>)[_delegate getModuleInstanceFromClass:moduleClass];
if (!module) {
module = [moduleClass new];
}
return module;
}
可见,最终还是通过代理的getModuleInstanceFromClass方法查找模块。前面已经分析过了,此方法最终也是调用RCTAppSetupDefaultModuleFromClass(moduleClass, self.delegate.dependencyProvider)方法查找,继续跟踪实现:
id<RCTTurboModule> RCTAppSetupDefaultModuleFromClass(Class moduleClass, id<RCTDependencyProvider> dependencyProvider)
{
// private block used to filter out modules depending on protocol conformance
NSArray * (^extractModuleConformingToProtocol)(RCTModuleRegistry *, Protocol *) =
^NSArray *(RCTModuleRegistry *moduleRegistry, Protocol *protocol) {
NSArray<NSString *> *classNames = @[];
if (protocol == @protocol(RCTImageURLLoader)) {
classNames = (dependencyProvider != nullptr) ? dependencyProvider.imageURLLoaderClassNames : @[];
} else if (protocol == @protocol(RCTImageDataDecoder)) {
classNames = (dependencyProvider != nullptr) ? dependencyProvider.imageDataDecoderClassNames : @[];
} else if (protocol == @protocol(RCTURLRequestHandler)) {
classNames = (dependencyProvider != nullptr) ? dependencyProvider.URLRequestHandlerClassNames : @[];
}
NSMutableArray *modules = [NSMutableArray new];
for (NSString *className in classNames) {
const char *cModuleName = [className cStringUsingEncoding:NSUTF8StringEncoding];
id moduleFromLibrary = [moduleRegistry moduleForName:cModuleName];
if (![moduleFromLibrary conformsToProtocol:protocol]) {
continue;
}
[modules addObject:moduleFromLibrary];
}
return modules;
};
// 省略......
// No custom initializer here.
return [moduleClass new];
}
三方TurboModule显然不符合上面的if判断逻辑,那么最终其实就是通过[moduleClass new]创建了RCTTurboModule的实例对象。
TurboModule注册
自动链接
首先打开react-native工程中提供的helloworld工程,配置文件react-native/private/helloworld/ios/Podfile:
target 'HelloWorld' do
config = use_native_modules!(['sh', '../scripts/config.sh'])
use_react_native!(
:path => "../../../packages/react-native",
# An absolute path to your application root.
:app_path => "#{Pod::Config.instance.installation_root}/.."
)
target 'HelloWorldTests' do
inherit! :complete
# Pods for testing
end
post_install do |installer|
# scripts/react_native_pods.rb
react_native_post_install(
installer,
config[:reactNativePath],
:mac_catalyst_enabled => false,
# :ccache_enabled => true
)
end
end
在以上的CocoaPod配置脚本中,use_native_modules! 函数是iOS端自动链接 (Autolinking) 的核心,它负责发现并链接所有第三方原生库,源码react-native/packages/react-native/scripts/cocoapods/autolinking.rb:
# 自动链接原生模块
#
# Parameters:
# - config_command: 用于获取应用程序当前配置的命令,例如 ['npx', '@react-native-community/cli', 'config'],
# 您可以根据需要覆盖此命令,以避免依赖项。例如 ['cat', 'your_config.json']
def use_native_modules!(config_command = $default_command)
return link_native_modules!(list_native_modules!(config_command))
end
继续查看list_native_modules!函数:
# 如果您使用这种方法来列出 React Native 模块,您的项目必须依赖于 @react-native-community/cli
#
# Parameters:
# - config_command: 用于获取应用程序当前配置的命令,例如 ['npx', '@react-native-community/cli', 'config']
def list_native_modules!(config_command)
if !(config_command.is_a? Array and config_command.size > 0)
# 省略日志打印......
exit(1)
end
# 1. 执行 CLI 命令获取项目配置
# 忽略标准错误输出,我们只关心标准输出和返回码。库可能会向标准错误输出警告信息,这会给 JSON 反序列化带来问题
json, _, status = Pod::Executable.capture_command(config_command[0], config_command[1..], capture: :both)
if not status.success?
# 省略日志输出......
exit(status.exitstatus)
end
config = JSON.parse(json)
# 2. 获取所有依赖包
packages = config["dependencies"]
ios_project_root = Pathname.new(config["project"]["ios"]["sourceDir"])
react_native_path = Pathname.new(config["reactNativePath"])
codegen_output_path = ios_project_root.join("build/generated/autolinking/autolinking.json")
# 3. 将配置写入 autolinking.json (供 Codegen 使用)
FileUtils.mkdir_p(File.dirname(codegen_output_path))
File.write(codegen_output_path, json)
found_pods = []
# 4. 遍历所有包,检查是否有 iOS 配置
packages.each do |package_name, package|
next unless package_config = package["platforms"]["ios"]
name = package["name"]
podspec_path = package_config["podspecPath"]
script_phases = package_config["scriptPhases"]
configurations = package_config["configurations"]
# 跳过没有 podspec 的包
if podspec_path.nil? || podspec_path.empty?
# 省略......
next
end
spec = Pod::Specification.from_file(podspec_path)
# 跳过不支持当前目标平台的环境。
next unless AutolinkingUtils.is_platform_supported?(current_target_definition, spec)
podspec_dir_path = Pathname.new(File.dirname(podspec_path))
relative_path = podspec_dir_path.relative_path_from ios_project_root
found_pods.push({
"configurations": configurations,
"name": name,
"root": package["root"],
"path": relative_path.to_path,
"podspec_path": podspec_path,
"script_phases": script_phases
})
end
if found_pods.size > 0
pods = found_pods.map { |p| p[:name] }.sort.to_sentence
Pod::UI.puts "Found #{found_pods.size} #{"module".pluralize(found_pods.size)} for target `#{current_target_definition.name}`"
end
return {
"ios_packages": found_pods,
"ios_project_root_path": ios_project_root.to_s,
"react_native_path": react_native_path.relative_path_from(ios_project_root).to_s
}
end
该函数主要是发现第三方库并返回扫描到的数据,生成autolinking.json。接下来查看link_native_modules!函数实现:
# Parameters:
# - config:
# - :ios_packages - React Native iOS 包数组, e.g. [{ package_name: "Foo", package: { .. }}, ...]
# - :ios_project_root_path - React Native 项目的 iOS 文件夹的绝对路径, e.g. /Users/foobar/project/rn_project/ios
# - :react_native_path - React Native 相对于项目根目录的相对路径, e.g. ./node_modules/react-native
def link_native_modules!(config)
Pod::UI.puts "link_native_modules! #{config}"
if !(
config[:ios_packages].is_a? Array and
config[:ios_project_root_path].is_a? String and
config[:react_native_path].is_a? String
)
# 省略日志打印......
exit(1)
end
# 提取项目根路径
ios_project_root = config[:ios_project_root_path]
# 提取包列表
packages = config[:ios_packages]
found_pods = []
# 遍历包并自动链接
packages.each do |package|
podspec_path = package[:podspec_path]
configurations = package[:configurations]
# 如果 podspec_path 为 nil 或空,添加警告到队列并继续处理下一个依赖
if podspec_path.nil? || podspec_path.empty?
# 省略日志打印......
next
end
# 读取并解析 .podspec 文件,获取包的完整规范信息。
spec = Pod::Specification.from_file(podspec_path)
# 检查该包是否支持当前平台(iOS),如果不支持,跳过该包
next unless AutolinkingUtils.is_platform_supported?(current_target_definition, spec)
# 我们想在当前 CocoaPods target 内部进行查找
# 以查看它是否已包含,这样做可以:
# 1. 给你一个机会预先定义它
# 2. 确保 CocoaPods 不会因为包含两次而崩溃
#
this_target = current_target_definition
existing_deps = current_target_definition.dependencies
# 跳过用户已经自己激活的依赖
next if existing_deps.find do |existing_dep|
existing_dep.name.split('/').first == spec.name
end
podspec_dir_path = Pathname.new(File.dirname(podspec_path))
relative_path = podspec_dir_path.relative_path_from ios_project_root
# 将找到的 React Native 模块注册到我们的 Pods 集合中(调用 CocoaPods 的 pod DSL 方法,添加依赖)
pod spec.name, :path => relative_path.to_path, :configurations => configurations
if package[:script_phases] && !this_target.abstract?
# 可以是一个对象,或对象数组
Array(package[:script_phases]).each do |phase|
# see https://www.rubydoc.info/gems/cocoapods-core/Pod/Podfile/DSL#script_phase-instance_method
# 获取完整的对象键
Pod::UI.puts "Adding a custom script phase for Pod #{spec.name}: #{phase["name"] || 'No name specified.'}"
# 支持传入相对于包根目录的路径
if phase["path"]
phase["script"] = File.read(File.expand_path(phase["path"], package[:root]))
phase.delete("path")
end
# 支持将执行位置转换为 symbol
phase["execution_position"] = phase["execution_position"]&.to_sym
phase = Hash[phase.map { |k, v| [k.to_sym, v] }]
script_phase phase
end
end
found_pods.push spec
end
if found_pods.size > 0
pods = found_pods.map { |p| p.name }.sort.to_sentence
# 省略打印......
end
return {
:reactNativePath => config[:react_native_path]
}
end
此函数就是在处理node_module中下载的依赖,最核心的其实就是pod spec.name, :path => relative_path.to_path, :configurations => configurations这行代码,它等价于我们习惯的类似配置:
pod 'react-native-camera', :path => '../node_modules/react-native-camera', :configurations => ['Debug', 'Release']
环境配置
弄清楚了use_native_modules!自动链接,接下来再看看use_react_native! 做了什么,源码react-native/packages/react-native/scripts/react_native_pods.rb:
# 设置所有 React Native 依赖的函数
#
# 参数
# - path: React Native 安装路径。
# - fabric_enabled: 是否应启用 Fabric。
# - new_arch_enabled: [已弃用] 是否应启用新架构。
# - :production [已弃用] 依赖是否必须安装到 Debug 或 Release 构建目标。
# - hermes_enabled: 是否应启用 Hermes。
# - app_path: React Native 应用的路径。新架构需要。
# - config_file_dir: `package.json` 文件的目录,新架构需要。
def use_react_native! (
path: "../node_modules/react-native",
fabric_enabled: false,
new_arch_enabled: NewArchitectureHelper.new_arch_enabled,
production: false, # 已弃用
hermes_enabled: true, # 已弃用。Hermes 是默认引擎,JSC 已移至社区支持
app_path: '..',
config_file_dir: '',
privacy_file_aggregation_enabled: true
)
# 1.初始化和环境变量设置
error_if_try_to_use_jsc_from_core()
warn_if_new_arch_disabled()
hermes_enabled= true
# 将 app_path 设置为环境变量,以便 podspecs 可以访问它
ENV['APP_PATH'] = app_path
ENV['REACT_NATIVE_PATH'] = path
# 如果用户想跳过从 CocoaPods 运行 Codegen 步骤,我们将 RCT_SKIP_CODEGEN 设置为 true。
# 这是我们从 CocoaPods 迁移过程中所需的
ENV['RCT_SKIP_CODEGEN'] = ENV['RCT_SKIP_CODEGEN'] == '1' || ENV['RCT_IGNORE_PODS_DEPRECATION'] == '1' ? '1' : '0'
ReactNativePodsUtils.check_minimum_required_xcode()
# 当前目标定义由 CocoaPods 提供,它指的是
# 调用了 `use_react_native!` 函数的目标。
ReactNativePodsUtils.detect_use_frameworks(current_target_definition)
# 2.清理和版本检测
CodegenUtils.clean_up_build_folder(path, $CODEGEN_OUTPUT_DIR)
# 我们也在第三方库中依赖此标志来正确安装依赖。
# 如果使用标志启用新架构,最好依赖并启用此环境标志。
relative_path_from_current = Pod::Config.instance.installation_root.relative_path_from(Pathname.pwd)
react_native_version = NewArchitectureHelper.extract_react_native_version(File.join(relative_path_from_current, path))
fabric_enabled = true
ENV['RCT_FABRIC_ENABLED'] = "1"
ENV['RCT_AGGREGATE_PRIVACY_FILES'] = privacy_file_aggregation_enabled ? "1" : "0"
ENV["RCT_NEW_ARCH_ENABLED"] = "1"
# 3.依赖配置(配置 React Native 核心库)
prefix = path
ReactNativePodsUtils.warn_if_not_on_arm64()
# 更新 ReactNativeDependencies,以便我们可以轻松地在源码和预构建之间切换
ReactNativeDependenciesUtils.setup_react_native_dependencies(prefix, react_native_version)
# 更新 ReactNativeCoreUtils,以便我们可以轻松地在源码和预构建之间切换
ReactNativeCoreUtils.setup_rncore(prefix, react_native_version)
Pod::UI.puts "Configuring the target with the New Architecture\n"
# 应包含在所有项目中的 Pods
pod 'FBLazyVector', :path => "#{prefix}/Libraries/FBLazyVector"
pod 'RCTRequired', :path => "#{prefix}/Libraries/Required"
pod 'RCTTypeSafety', :path => "#{prefix}/Libraries/TypeSafety", :modular_headers => true
pod 'React', :path => "#{prefix}/"
if !ReactNativeCoreUtils.build_rncore_from_source()
pod 'React-Core-prebuilt', :podspec => "#{prefix}/React-Core-prebuilt.podspec", :modular_headers => true
end
pod 'React-Core', :path => "#{prefix}/"
pod 'React-CoreModules', :path => "#{prefix}/React/CoreModules"
pod 'React-RCTRuntime', :path => "#{prefix}/React/Runtime"
# 省略部分......
pod 'React-jsi', :path => "#{prefix}/ReactCommon/jsi"
pod 'RCTSwiftUI', :path => "#{prefix}/ReactApple/RCTSwiftUI"
pod 'RCTSwiftUIWrapper', :path => "#{prefix}/ReactApple/RCTSwiftUIWrapper"
if hermes_enabled
setup_hermes!(:react_native_path => prefix)
end
pod 'React-jsiexecutor', :path => "#{prefix}/ReactCommon/jsiexecutor"
# 省略部分......
pod 'ReactCommon/turbomodule/core', :path => "#{prefix}/ReactCommon", :modular_headers => true
pod 'React-NativeModulesApple', :path => "#{prefix}/ReactCommon/react/nativemodule/core/platform/ios", :modular_headers => true
pod 'Yoga', :path => "#{prefix}/ReactCommon/yoga", :modular_headers => true
setup_fabric!(:react_native_path => prefix)
setup_bridgeless!(:react_native_path => prefix, :use_hermes => hermes_enabled)
if ReactNativeDependenciesUtils.build_react_native_deps_from_source()
pod 'DoubleConversion', :podspec => "#{prefix}/third-party-podspecs/DoubleConversion.podspec"
pod 'glog', :podspec => "#{prefix}/third-party-podspecs/glog.podspec"
pod 'boost', :podspec => "#{prefix}/third-party-podspecs/boost.podspec"
pod 'fast_float', :podspec => "#{prefix}/third-party-podspecs/fast_float.podspec"
pod 'fmt', :podspec => "#{prefix}/third-party-podspecs/fmt.podspec", :modular_headers => true
pod 'RCT-Folly', :podspec => "#{prefix}/third-party-podspecs/RCT-Folly.podspec", :modular_headers => true
pod 'SocketRocket', "~> #{Helpers::Constants::socket_rocket_config[:version]}", :modular_headers => true
else
# Install prebuilt React Native Core and React Native Dependencies
ReactNativeCoreUtils.rncore_log("Using React Native Core and React Native Dependencies prebuilt versions.")
pod 'ReactNativeDependencies', :podspec => "#{prefix}/third-party-podspecs/ReactNativeDependencies.podspec", :modular_headers => true
if !ReactNativeCoreUtils.build_rncore_from_source()
pod 'React-Core-prebuilt', :podspec => "#{prefix}/React-Core-prebuilt.podspec", :modular_headers => true
end
end
pod 'ReactCodegen', :path => $CODEGEN_OUTPUT_DIR, :modular_headers => true
pod 'ReactAppDependencyProvider', :path => $APP_DEPENDENCY_PROVIDER_OUTPUT_DIR, :modular_headers => true
# 不需要,但 run_codegen 期望设置此值。
folly_config = get_folly_config()
# 运行 Codegen:扫描所有 TurboModule 和 Fabric 组件规范,生成 C++ 绑定代码
run_codegen!(
app_path,
config_file_dir,
:new_arch_enabled => NewArchitectureHelper.new_arch_enabled,
:disable_codegen => ENV['DISABLE_CODEGEN'] == '1',
:react_native_path => prefix,
:fabric_enabled => fabric_enabled,
:hermes_enabled => hermes_enabled,
:codegen_output_dir => $CODEGEN_OUTPUT_DIR,
:package_json_file => File.join(__dir__, "..", "package.json"),
:folly_version => folly_config[:version]
)
pods_to_update = LocalPodspecPatch.pods_to_update(:react_native_path => prefix)
if !pods_to_update.empty?
if Pod::Lockfile.public_instance_methods.include?(:detect_changes_with_podfile)
Pod::Lockfile.prepend(LocalPodspecPatch)
else
# 省略打印......
end
end
end
此函数太长,这里省略了大部分的依赖配置项。这里总结一下该函数做的事:
-
配置环境:设置环境变量、检查 Xcode 版本
-
添加 60+ 个 Pod 依赖:涵盖从核心到工具的所有模块
-
设置新架构:Fabric、TurboModule、Bridgeless
-
配置 Hermes 引擎
-
运行 Codegen:自动生成 C++ 绑定代码
-
处理预构建二进制:加速编译
总的说,这是 React Native iOS 项目的"一键配置"函数,它设置了新架构所需的所有依赖和工具链。
代码生成
继续分析run_codegen!函数,探索iOS端,是如何自动生成代码的。源码react-native/packages/react-native/scripts/cocoapods/codegen.rb:
def run_codegen!(
app_path, # 应用根目录路径(必需)
config_file_dir, # 配置文件目录(必需)
new_arch_enabled: true, # 是否启用新架构(默认 true,但当前未使用)
disable_codegen: false, # 是否禁用 Codegen(默认 false)
react_native_path: "../node_modules/react-native", # React Native 源码路径
fabric_enabled: false, # 是否启用 Fabric(默认 false)
hermes_enabled: true, # 是否启用 Hermes(默认 true)
codegen_output_dir: 'build/generated/ios', # Codegen 输出目录
config_key: 'codegenConfig', # package.json 中配置键名
package_json_file: '~/app/package.json', # package.json 文件路径(当前未使用)
folly_version: Helpers::Constants.folly_config()[:version], # Folly 库版本
codegen_utils: CodegenUtils.new() # CodegenUtils 实例(可注入用于测试)
)
if ENV["RCT_SKIP_CODEGEN"] == "1"
return
end
codegen_utils.use_react_native_codegen_discovery!(
disable_codegen,
app_path,
:react_native_path => react_native_path,
:fabric_enabled => fabric_enabled,
:hermes_enabled => hermes_enabled,
:config_file_dir => config_file_dir,
:codegen_output_dir => codegen_output_dir,
:config_key => config_key,
:folly_version => folly_version
)
end
可以看到,实际上是委托给 CodegenUtils 执行代码生成。继续查看源码react-native/packages/react-native/scripts/cocoapods/codegen_utils.rb:
def use_react_native_codegen_discovery!(
codegen_disabled,
app_path,
react_native_path: "../node_modules/react-native",
fabric_enabled: false,
hermes_enabled: true,
config_file_dir: '',
codegen_output_dir: 'build/generated/ios',
config_key: 'codegenConfig',
folly_version: Helpers::Constants.folly_config[:version],
codegen_utils: CodegenUtils.new(),
file_manager: File,
logger: CodegenUtils::UI
)
return if codegen_disabled
if CodegenUtils.react_codegen_discovery_done()
logger.puts("Skipping use_react_native_codegen_discovery.")
return
end
if !app_path
logger.warn("Error: app_path is required for use_react_native_codegen_discovery.")
logger.warn("If you are calling use_react_native_codegen_discovery! in your Podfile, please remove the call and pass `app_path` and/or `config_file_dir` to `use_react_native!`.")
abort
end
relative_installation_root = Pod::Config.instance.installation_root.relative_path_from(Pathname.pwd)
out = Pod::Executable.execute_command(
'node',
[
"#{relative_installation_root}/#{react_native_path}/scripts/generate-codegen-artifacts.js",
"-p", "#{app_path}",
"-o", Pod::Config.instance.installation_root,
"-t", "ios",
])
Pod::UI.puts out;
# 标记完成,防止重复执行
CodegenUtils.set_react_codegen_discovery_done(true)
end
此处实际上仍然是调用JS工具来生成代码,这里执行的命令,相当于:
node node_modules/react-native/scripts/generate-codegen-artifacts.js \
-p /narwal/Your Project/helloworld \
-o /narwal/Your Project/helloworld/ios \
-t ios
继续跟踪react-native/packages/react-native/scripts/generate-codegen-artifacts.js:
const executor = require('./codegen/generate-artifacts-executor');
// 省略......
executor.execute(argv.path, argv.targetPlatform, argv.outputPath, argv.source);
查看execute实现,源码react-native/packages/react-native/scripts/codegen/generate-artifacts-executor/index.js:
/**
* 此函数是代码生成器的入口点。它执行以下操作:
* - 读取 package.json 文件
* - 提取库信息
* - 设置命令行界面以生成代码
* - 生成代码
*
* @param projectRoot:包含应用程序源代码的目录,package.json 文件位于该目录下。
* @param baseOutputPath:代码生成器的基本输出路径。
* @param targetPlatform:目标平台。支持的值包括:“android”、“ios”、“all”。
* @param source:调用代码生成器的来源。支持的值包括:“app”、“library”。
* @throws 如果找不到 React Native 的配置文件。
* @throws 如果在文件中找不到代码生成器配置。
* @throws 如果找不到代码生成器的命令行工具。
*/
function execute(
projectRoot /*: string */,
targetPlatform /*: string */,
optionalBaseOutputPath /*: ?string */,
source /*: string */,
runReactNativeCodegen /*: boolean */ = true,
) {
try {
codegenLog(`Analyzing ${path.join(projectRoot, 'package.json')}`);
const supportedPlatforms = ['android', 'ios'];
if (
targetPlatform !== 'all' &&
!supportedPlatforms.includes(targetPlatform)
) {
throw new Error(
`Invalid target platform: ${targetPlatform}. Supported values are: ${supportedPlatforms.join(
', ',
)}, all`,
);
}
// 1.读取应用的 package.json
const pkgJson = readPkgJsonInDirectory(projectRoot);
if (runReactNativeCodegen) {
// 2.如果 Codegen CLI 未构建,先执行 yarn build
buildCodegenIfNeeded();
}
const platforms =
targetPlatform === 'all' ? supportedPlatforms : [targetPlatform];
// NOTE: We cache the external libraries search (which may not run) across platforms to not change previous behaviour
const externalLibrariesCache /*: { current?: ?Array<$FlowFixMe> } */ = {};
for (const platform of platforms) {
// NOTE: This needs to be computed per-platform since `platform` can alter the path via a `package.json:codegenConfig.outputDir[platform]` override
const baseOutputPath = computeBaseOutputPath(
projectRoot,
optionalBaseOutputPath,
pkgJson,
platform,
);
const reactNativeConfig = readReactNativeConfig(
projectRoot,
baseOutputPath,
);
// 3.扫描所有依赖库,找出包含 codegenConfig 的库
const codegenEnabledLibraries = findCodegenEnabledLibraries(
pkgJson,
projectRoot,
baseOutputPath,
reactNativeConfig,
externalLibrariesCache,
);
if (codegenEnabledLibraries.length === 0) {
codegenLog('No codegen-enabled libraries found.', true);
}
const disabledLibraries = findDisabledLibrariesByPlatform(
reactNativeConfig,
platform,
);
const libraries = codegenEnabledLibraries.filter(
({name}) => !disabledLibraries.includes(name),
);
const outputPath = computeOutputPath(
projectRoot,
baseOutputPath,
pkgJson,
platform,
);
const reactCodegenOutputPath =
platform === 'android'
? outputPath
: path.join(outputPath, 'ReactCodegen');
if (runReactNativeCodegen) {
// 4.为每个库生成 Schema JSON(描述 TurboModule/Fabric 接口)
const schemaInfos = generateSchemaInfos(libraries);
// 5.根据 Schema 生成 C++/ObjC++ 代码
generateNativeCode(
reactCodegenOutputPath,
schemaInfos.filter(schemaInfo =>
mustGenerateNativeCode(projectRoot, schemaInfo),
),
pkgJsonIncludesGeneratedCode(pkgJson),
platform,
);
}
if (source === 'app' && platform !== 'android') {
// 6.生成 Fabric 第三方组件注册代码
// These components are only required by apps, not by libraries and are Apple specific.
generateRCTThirdPartyComponents(libraries, reactCodegenOutputPath);
// 7.生成 TurboModule 提供者代码
generateRCTModuleProviders(
projectRoot,
pkgJson,
libraries,
reactCodegenOutputPath,
);
// 8.生成自定义 URL Scheme 处理器
generateCustomURLHandlers(libraries, reactCodegenOutputPath);
generateUnstableModulesRequiringMainQueueSetupProvider(
libraries,
reactCodegenOutputPath,
);
// 9.生成应用级依赖提供者
generateAppDependencyProvider(
path.join(outputPath, 'ReactAppDependencyProvider'),
);
// 10.生成 ReactCodegen.podspec
generateReactCodegenPodspec(
projectRoot,
pkgJson,
reactCodegenOutputPath,
baseOutputPath,
);
generatePackageSwift(
projectRoot,
outputPath,
findReactNativeRootPath(projectRoot),
);
}
cleanupEmptyFilesAndFolders(outputPath);
}
} catch (err) {
codegenLog(err);
process.exitCode = 1;
}
codegenLog('Done.', true);
return;
}
我已经为上面10个比较关键的步骤增加了注释,流程还是很清晰的。由于这篇文章主要是研究iOS端TurboModule,那么需要重点关注一下generateRCTModuleProviders和generateAppDependencyProvider两个函数的实现。
源码react-native/packages/react-native/scripts/codegen/generate-artifacts-executor/generateRCTModuleProviders.js:
const MODULE_PROVIDERS_H_TEMPLATE_PATH = path.join(
TEMPLATES_FOLDER_PATH,
'RCTModuleProvidersH.template',
);
const MODULE_PROVIDERS_MM_TEMPLATE_PATH = path.join(
TEMPLATES_FOLDER_PATH,
'RCTModuleProvidersMM.template',
);
function generateRCTModuleProviders(
projectRoot /*: string */,
pkgJson /*: $FlowFixMe */,
libraries /*: $ReadOnlyArray<$FlowFixMe> */,
outputDir /*: string */,
) {
// 1.创建输出目录
fs.mkdirSync(outputDir, {recursive: true});
// 2.生成头文件 RCTModuleProviders.h
codegenLog('Generating RCTModulesProvider.h');
const templateH = fs.readFileSync(MODULE_PROVIDERS_H_TEMPLATE_PATH, 'utf8');
const finalPathH = path.join(outputDir, 'RCTModuleProviders.h');
fs.writeFileSync(finalPathH, templateH);
codegenLog(`Generated artifact: ${finalPathH}`);
codegenLog('Generating RCTModuleProviders.mm');
// 3.初始化一个模块收集容器
let modulesInLibraries /*: {[string]: Array<$FlowFixMe>} */ = {};
let app = pkgJson.codegenConfig
? {config: pkgJson.codegenConfig, libraryPath: projectRoot}
: null;
// 4.将应用自身的 TurboModule 加入处理列表
const moduleLibraries = libraries
.concat(app)
.filter(Boolean)
.filter(({config, libraryPath}) => {
// 5.过滤出需要处理的模块库
if (isReactNativeCoreLibrary(config.name) || config.type === 'components') {
return false;
}
return true;
});
// 6.收集旧 API 配置(modulesProvider 字段)
moduleLibraries.forEach(({config, libraryPath}) => {
const libraryName = JSON.parse(
fs.readFileSync(path.join(libraryPath, 'package.json'), 'utf8'),
).name;
if (config.ios?.modulesProvider) {
modulesInLibraries[libraryName] = Object.keys(
config.ios?.modulesProvider,
).map(moduleName => {
return {
moduleName,
className: config.ios?.modulesProvider[moduleName],
};
});
}
});
// 7.收集新 API 配置(modules 字段)
const iosAnnotations = parseiOSAnnotations(moduleLibraries);
for (const [libraryName, {modules: moduleAnnotationMap}] of Object.entries(
iosAnnotations,
)) {
for (const [moduleName, annotation] of Object.entries(
moduleAnnotationMap,
)) {
if (annotation.className) {
modulesInLibraries[libraryName] = modulesInLibraries[libraryName] || [];
modulesInLibraries[libraryName].push({
moduleName,
className: annotation.className,
});
}
}
}
// 8.生成 Objective-C 映射代码
const modulesMapping = Object.keys(modulesInLibraries)
.flatMap(library => {
const modules = modulesInLibraries[library];
return modules.map(({moduleName, className}) => {
return `\t\t@"${moduleName}": @"${className}", // ${library}`;
});
})
.join('\n');
// 9.将映射代码注入模板并写入文件
const templateMM = fs
.readFileSync(MODULE_PROVIDERS_MM_TEMPLATE_PATH, 'utf8')
.replace(/{moduleMapping}/, modulesMapping);
const finalPathMM = path.join(outputDir, 'RCTModuleProviders.mm');
fs.writeFileSync(finalPathMM, templateMM);
codegenLog(`Generated artifact: ${finalPathMM}`);
}
现在我们来看一个生成的RCTModuleProviders实例,源码Your Project/ios/build/generated/ios/ReactCodegen/RCTModuleProviders.mm:
@implementation RCTModuleProviders
+ (NSDictionary<NSString *, id<RCTModuleProvider>> *)moduleProviders
{
static NSDictionary<NSString *, id<RCTModuleProvider>> *providers = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSDictionary<NSString *, NSString *> * moduleMapping = @{
@"RNCWebViewModule": @"RNCWebViewModule", // react-native-webview
};
NSMutableDictionary *dict = [[NSMutableDictionary alloc] initWithCapacity:moduleMapping.count];
for (NSString *key in moduleMapping) {
NSString * moduleProviderName = moduleMapping[key];
Class klass = NSClassFromString(moduleProviderName);
if (!klass) {
RCTLogError(@"Module provider %@ cannot be found in the runtime", moduleProviderName);
continue;
}
id instance = [klass new];
if (![instance respondsToSelector:@selector(getTurboModule:)]) {
RCTLogError(@"Module provider %@ does not conform to RCTModuleProvider", moduleProviderName);
continue;
}
[dict setObject:instance forKey:key];
}
providers = dict;
});
return providers;
}
这是一个懒加载 + 单例模式的实现。使用NSClassFromString通过字符串反射查找类,然后[klass new]实例化 Provider 并验证协议。
最后再来看generateAppDependencyProvider实现,源码react-native/packages/react-native/scripts/codegen/generate-artifacts-executor/generateAppDependencyProvider.js:
function generateAppDependencyProvider(outputDir /*: string */) {
fs.mkdirSync(outputDir, {recursive: true});
codegenLog('Generating RCTAppDependencyProvider');
const templateH = fs.readFileSync(
APP_DEPENDENCY_PROVIDER_H_TEMPLATE_PATH,
'utf8',
);
const finalPathH = path.join(outputDir, 'RCTAppDependencyProvider.h');
fs.writeFileSync(finalPathH, templateH);
codegenLog(`Generated artifact: ${finalPathH}`);
const templateMM = fs.readFileSync(
APP_DEPENDENCY_PROVIDER_MM_TEMPLATE_PATH,
'utf8',
);
const finalPathMM = path.join(outputDir, 'RCTAppDependencyProvider.mm');
fs.writeFileSync(finalPathMM, templateMM);
codegenLog(`Generated artifact: ${finalPathMM}`);
// Generate the podspec file
const templatePodspec = fs
.readFileSync(APP_DEPENDENCY_PROVIDER_PODSPEC_TEMPLATE_PATH, 'utf8')
.replace(/{react-native-version}/, packageJson.version)
.replace(/{react-native-licence}/, packageJson.license);
const finalPathPodspec = path.join(
outputDir,
'ReactAppDependencyProvider.podspec',
);
fs.writeFileSync(finalPathPodspec, templatePodspec);
codegenLog(`Generated podspec: ${finalPathPodspec}`);
}
此方法生成一个 统一的依赖提供者类(RCTAppDependencyProvider),它将之前生成的所有 Codegen 产物(TurboModule、Fabric 组件、URL 处理器等)聚合到一个统一接口中,供 React Native 运行时使用。
我们直接看生成之后的文件,源码Your Project/ios/build/generated/ios/ReactAppDependencyProvider/RCTAppDependencyProvider.mm:
@implementation RCTAppDependencyProvider
- (nonnull NSArray<NSString *> *)URLRequestHandlerClassNames {
return RCTModulesConformingToProtocolsProvider.URLRequestHandlerClassNames;
}
- (nonnull NSArray<NSString *> *)imageDataDecoderClassNames {
return RCTModulesConformingToProtocolsProvider.imageDataDecoderClassNames;
}
- (nonnull NSArray<NSString *> *)imageURLLoaderClassNames {
return RCTModulesConformingToProtocolsProvider.imageURLLoaderClassNames;
}
- (nonnull NSArray<NSString *> *)unstableModulesRequiringMainQueueSetup {
return RCTUnstableModulesRequiringMainQueueSetupProvider.modules;
}
- (nonnull NSDictionary<NSString *,Class<RCTComponentViewProtocol>> *)thirdPartyFabricComponents {
return RCTThirdPartyComponentsProvider.thirdPartyFabricComponents;
}
- (nonnull NSDictionary<NSString *, id<RCTModuleProvider>> *)moduleProviders {
return RCTModuleProviders.moduleProviders;
}
@end
到此,基本上就闭环了。在本文开头,我们就分析了delegate.dependencyProvider = RCTAppDependencyProvider(),而在getModuleProvider方法实现中,self.dependencyProvider != nullptr) ? self.dependencyProvider.moduleProviders[providerName] : nullptr一行,正是从RCTModuleProviders.moduleProviders中查找模块。



