iOS 开源库源码分析之 react-native

此文基于 react-native-0.53.0

react-native 是 facebook 于 2015.3.27 发布的一款构建移动 native app 的框架,使用的 JavaScript 和 React。

本文主要讲述 react-native 在 iOS 端的内容,包括 react-native 的加载流程,JS 与 native 互调的过程,UI 渲染的步骤以及如何传递 native 事件。

加载流程

  • RCTBridge 的初始化
  • batchedBridge(RCTCxxBridge) 调用 start
  • 通过 RCTJavaScriptLoader 的 loadBundleAtURL 方法把 url 解析成 data
  • 获取到数据后,调用 RCTCxxBridge 的 executeApplicationScript
  • 调用 JSCExecutor 的 loadApplicationScript 方法
  • 在 loadApplicationScript 里面会调用 JSCHelplers 的 evaluateScript 方法,传入 JSContextRef 和前面的 data 转化成的脚本字符串 script,以及 sourceURL,这里其实最终调用的就是 JavaScriptCore 的 JSBase 类的 JSEvaluateScript 函数

JS调用native方法

native 方法能够被 JS 调用

  • 一个 native 的类能够被访问首先需要注册进入 JS,是通过一个宏 RCT_EXPORT_MODULE(js_name),会把 js_name(类名)加入在 RCTBridge 的 RCTModuleClasses(数组,模块配置列表)中

  • 然后会把 RCTModuleClasses 转化为 RCTCxxBridge 的 _moduleDataByID

  • 接着在 RCTCxxBridge 的_buildModuleRegistry中会构建出 ModuleRegistry 对象,传入_moduleDataByID作为其成员变量modules_

  • Objective-C 的对象能够被 JS 访问得益于此,

installGlobalProxy(m_context, "nativeModuleProxy",
                           exceptionWrapMethod<&JSCExecutor::getNativeModule>());

最终这里会调用 JSObjectSetProperty(),传入类名和访问的 callback。

这样 JS 端在通过

var RCTAlertManager = require('NativeModules').AlertManager;

访问的时候就会回调 JSCExecutor 的 getNativeModule 方法,最终调用 JSCNativeModules 的 getModule,在这里会从 m_moduleRegistry->getConfig(name) 获取对应的结果,ModuleRegistry 的查找就是根据前面modules_模块配置列表找到对应的 RCTAlertManager

  • ObjC 的方法能够被 JS 调用则是这样,JSCHelpers 的 installGlobalFunction(),可以设置 JS 的 function callback,JS 可以同步(在 JS 线程里面)或者异步(切换到指定线程)调用 native 方法,分别是 nativeCallSyncHook 和 nativeFlushQueueImmediate(MessageQueue.js 的 enqueueNativeCall 调用 callback 对象),这两个方法定义在NativeModules.js,callback 实现在 native 端

installNativeHook 最终是把 exceptionWrapMethod(nativeCallSyncHook) callback设置到对应的 jsName(nativeCallSyncHook) 上去,这里通过的是 JSC_JSObjectMakeFunctionWithCallback 方法(也就是 JSObjectRef 的JSObjectMakeFunctionWithCallback())

    installNativeHook<&JSCExecutor::nativeFlushQueueImmediate>("nativeFlushQueueImmediate");

    void JSCExecutor::installNativeHook(const char* name) {
      installGlobalFunction(m_context, name, exceptionWrapMethod<method>());
    }

    void installGlobalFunction(
        JSGlobalContextRef ctx,
        const char* name,
        JSObjectCallAsFunctionCallback callback) {
      String jsName(ctx, name);
      JSObjectRef functionObj = JSC_JSObjectMakeFunctionWithCallback(
        ctx, jsName, callback);
      Object::getGlobalObject(ctx).setProperty(jsName, Value(ctx, functionObj));
    }

对于 exceptionWrapMethod 里面会把 installNativeHook 传进来的执行者和方法来调用 (executor->*method)(argumentCount, arguments),这样调用 callback 的时候就会调用 JSCExecutor 的 nativeCallSyncHook 或者 nativeFlushQueueImmediate 方法。
```
template
inline JSObjectCallAsFunctionCallback exceptionWrapMethod() {
struct funcWrapper {
static JSValueRef call(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef *exception) {
try {
auto executor = Object::getGlobalObject(ctx).getPrivate();
if (executor && executor->getJavaScriptContext()) { // Executor not invalidated
return (executor->*method)(argumentCount, arguments);
}
} catch (...) {
*exception = translatePendingCppExceptionToJSError(ctx, function);
}
return Value::makeUndefined(ctx);
}
};

    return &funcWrapper::call;
top Created with Sketch.