深入领略React Native焦点道理(React Native的桥接(

在这篇文章之前我们假设你已经相识了React Native的基本常识,我们会重点存眷当native和JavaScript举办信息交换时的内部运行道理。

主线程

在开始之前,我们需要知道在React Native中有三个主要的线程:

shadow queue:认真机关事情

main thread:UIKit 在这个线程事情(译者注:UI Manager线程,可以当作主线程,主要认真页面交互和控件绘制的逻辑)

JavaScript thread:运行JS代码的线程

别的,一般环境下每个native模块都有本身的GCD行列,除非有非凡说明(后头会表明)

*shadow queue其实更像一个GCD行列而不是线程

Native模块

假如你还不知道怎么建设一个Native模块,我推荐你去阅读一下文档

这是一个native模块Person的例子,它既受JavaScript的挪用,也可以挪用JavaScript

@interface Person : NSObject <RCTBridgeModule> @end @implementation Logger RCT_EXPORT_MODULE() RCT_EXPORT_METHOD(greet:(NSString *)name) { NSLog(@"Hi, %@!", name); [_bridge.eventDispatcher sendAppEventWithName:@"greeted" body:@{ @"name": name }]; } @end

我们重点存眷RCT_EXPORT_MODULE和RCT_EXPORT_METHOD这两个宏,它们扩展成什么,它们的脚色是什么,它们是如何运行的。

RCT_EXPORT_MODULE([js_name])

正如这个要领的名字那样,它export出你的module,可是在这个特定的上下文中export是什么意思呢,它意味着桥接知道你的模块。

它的界说实际上很是简朴:

#define RCT_EXPORT_MODULE(js_name) \ RCT_EXTERN void RCTRegisterModule(Class); \ + (NSString \*)moduleName { return @#js_name; } \ + (void)load { RCTRegisterModule(self); }

它做了以下事情:

首先声明RCTRegisterModule为外部函数,意味着这个函数的实现对付编译器不行见,可是在链接阶段可用

声明一个要领moduleName,返回可选的宏参数js_name,这样这个模块在JS中具有和Objective-C中纷歧样的类名

声明一个load要领(当app加载到内存中后,每个类的load要领城市被挪用),load要领挪用RCTRegisterModule,然后桥接才知道这个袒暴露来的模块

RCT_EXPORT_METHOD(method)

这个宏更有趣,它没有在你的method中增加任何对象,除了声明指定的要领外,它还建设了一个新要领。新要领如下所示:

+ (NSArray *)__rct_export__120 { return @[ @"", @"log:(NSString *)message" ]; }

它是通过将前缀(__rct_export__)和可选的js_name(本例子为空)和声明的行号以及__COUNTER__宏组成。

这个要领的目标是返回一个包括可选js_name和method签名的数组,这个js_name的浸染是制止要领定名斗嘴。

Runtime

这整个配置仅仅是为了给桥接提供信息,让它可以找到export出来的所有对象,modules和methods,可是这些都是在加载的时候产生的,此刻我们来看看运行的时候是怎么利用的。

这是桥接初始化时的依赖干系图:

深入明确React Native核心原理(React Native的桥接(

初始化模块

RCTRegisterModule所做的事就是把类推进数组,这样在实例化一个新的桥接的时候就能找到这个类。桥接遍历数组中的所有模块,为每个模块建设一个实例,在桥接何处存储一个实例的引用,同时给这个模块实例一个桥接的引用(所以我们能双方都相互挪用),然后查抄这个模块实例是否有指定要在哪个行列运行,不然给它一个新行列,与其他模块分隔:

NSMutableDictionary *modulesByName; // = ... for (Class moduleClass in RCTGetModuleClasses()) { // ... module = [moduleClass new]; if ([module respondsToSelector:@selector(setBridge:)]){ module.bridge = self; modulesByName[moduleName] = module; // ... }

设置模块

一旦我们有了这些modules,在靠山线程中,我们列出每个module的所有methods,然后挪用以__rct__export__开头的methods,我们获得一个method签名的字符串。这很重要因为我们此刻知道了参数的实际范例,在运行的时候我们只知道个中一个参数是id,可是通过这个途径我们可以知道这个id实际上是NSString *

unsigned int methodCount; Method *methods = class_copyMethodList(moduleClass, &methodCount); for (unsigned int i = 0; i < methodCount; i++) { Method method = methods[i]; SEL selector = method_getName(method); if ([NSStringFromSelector(selector) hasPrefix:@"__rct_export__"]) { IMP imp = method_getImplementation(method); NSArray *entries = ((NSArray *(*)(id, SEL))imp)(_moduleClass, selector); //... [moduleMethods addObject:/* Object representing the method */]; } }

配置JavaScript执行器

JS执行器有一个 -setUp 要领答允它做更巨大的事情,譬喻在靠山线程初始化JS代码,这同时节省了一些事情,因为只有活泼的执行器会接管 setUp 要领的挪用,而不是所有的执行器:

JSGlobalContextRef ctx = JSGlobalContextCreate(NULL); _context = [[RCTJavaScriptContext alloc] initWithJSContext:ctx];

注入JSON设置

JSON设置仅包括我们的module,譬喻:

深入明确React Native核心原理(React Native的桥接(

这个设置信息作为全局变量存储在JavaScript虚拟机,所以当JS何处的桥接初始化后它可以用这个信息来建设modules

加载JavaScript代码 

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wsjyws.html