本文是在看过一位大神的总结之后,又亲自根据文章看了一遍源码,算是对大神的文章做一些补充。下面给出文章链接。阅读本文最好参考文章的函数调用顺序以及源码会更加明朗。
http://www.cocoachina.com/programmer/20170505/19189.html
一.RN初始化过程
React-Native本身是通过iOS提供的JavaScriptCore框架进行通信的。
下面我们来看一下加载流程。首先找到RN的入口,也就是创建RCTRootView的地方:
rootView = RCTRootView.init(bundleURL: jsPath, moduleName:"BindPhone", initialProperties: dict, launchOptions: nil)
这个方法内部会调到RCTRootView的
- (instancetype)initWithBundleURL:(NSURL *)bundleURL
moduleName:(NSString *)moduleName
initialProperties:(NSDictionary *)initialProperties
launchOptions:(NSDictionary *)launchOptions
{
RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:bundleURL
moduleProvider:nil
launchOptions:launchOptions];
return [self initWithBridge:bridge moduleName:moduleName initialProperties:initialProperties];
}
在这个方法内部会创建一个RCTBridge对象并且会调用另外一个构造函数,我们先来看RCTBridge初始化的时候做了什么?顺着方法调用链,我们来到了RCTBridge的setUp方法
- (void)setUp
{
RCT_PROFILE_BEGIN_EVENT(0, @"-[RCTBridge setUp]", nil);
_performanceLogger = [RCTPerformanceLogger new];
[_performanceLogger markStartForTag:RCTPLBridgeStartup];
[_performanceLogger markStartForTag:RCTPLTTI];
// Only update bundleURL from delegate if delegate bundleURL has changed
NSURL *previousDelegateURL = _delegateBundleURL;
_delegateBundleURL = [self.delegate sourceURLForBridge:self];
if (_delegateBundleURL && ![_delegateBundleURL isEqual:previousDelegateURL]) {
_bundleURL = _delegateBundleURL;
}
// Sanitize the bundle URL
_bundleURL = [RCTConvert NSURL:_bundleURL.absoluteString];
[self createBatchedBridge];
[self.batchedBridge start];
RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
}
在这个函数里我们发现RN会创建一个RCTBatchedBridge,并且把RCTBatchedBridge作为自己的一个属性。在RCTBatchedBridge内部会把当前的RCTBridge对象作为一个全局静态属性。
接下来就会执行RCTBatchedBridge的start方法。由于这个start方法比较长,我们只截取一部分来说。
- (void)start
{
[[NSNotificationCenter defaultCenter]
postNotificationName:RCTJavaScriptWillStartLoadingNotification
object:_parentBridge userInfo:@{@"bridge": self}];
dispatch_queue_t bridgeQueue = dispatch_queue_create("com.facebook.react.RCTBridgeQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t initModulesAndLoadSource = dispatch_group_create();
// Asynchronously load source code
dispatch_group_enter(initModulesAndLoadSource);
__weak RCTBatchedBridge *weakSelf = self;
__block NSData *sourceCode;
[self loadSource:^(NSError *error, NSData *source, __unused int64_t sourceLength) {
if (error) {
RCTLogWarn(@"Failed to load source: %@", error);
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf stopLoadingWithError:error];
});
}
sourceCode = source;
dispatch_group_leave(initModulesAndLoadSource);
} onProgress:^(RCTLoadingProgress *progressData) {
#ifdef RCT_DEV
RCTDevLoadingView *loadingView = [weakSelf moduleForClass:[RCTDevLoadingView class]];
[loadingView updateProgress:progressData];
#endif
}];
// Synchronously initialize all native modules that cannot be loaded lazily
[self initModulesWithDispatchGroup:initModulesAndLoadSource];
}
首先会触发一个RCTJavaScriptWillStartLoadingNotification通知,从通知命名一看便知这里是告诉我们即将要加载JavaScript代码。然后就会去调用loadSource方法,要注意这里loadSource有同步加载和异步加载两种情况,如果同步加载失败就会去调用异步加载的方法,最后加载得到的是一个NSData对象。
接下来会调用initModulesWithDispatchGroup方法并且把一个GCD group作为参数传进去,通过名字便可知这个方法是做一些OC原生Modules的初始化工作,也就是说RN在这个方法里面会拿到OC要暴露给RN的类和方法。我们看一下initModulesWithDispatchGroup方法:
for (Class moduleClass in RCTGetModuleClasses()) {
NSString *moduleName = RCTBridgeModuleNameForClass(moduleClass);
// Check for module name collisions
RCTModuleData *moduleData = moduleDataByName[moduleName];
if (moduleData) {
if (moduleData.hasInstance) {
// Existing module was preregistered, so it takes precedence
continue;
} else if ([moduleClass new] == nil) {
// The new module returned nil from init, so use the old module
continue;
} else if ([moduleData.moduleClass new] != nil) {
// Both modules were non-nil, so it's unclear which should take precedence
RCTLogError(@"Attempted to register RCTBridgeModule class %@ for the "
"name '%@', but name was already registered by class %@",
moduleClass, moduleName, moduleData.moduleClass);
}
}
// Instantiate moduleData (TODO: can we defer this until config generation?)
moduleData = [[RCTModuleData alloc] initWithModuleClass:moduleClass
bridge:self];
moduleDataByName[moduleName] = moduleData;
[moduleClassesByID addObject:moduleClass];
[moduleDataByID addObject:moduleData];
}
RCTGetModuleClasses()会把遵守RCTBridgeModule的类都装到数组里并返回,然后根据moduleName创建ModuleData并缓存到全局的字典中。还会把moduleClass放到moduleClassesByID,moduleData放到moduleDataByID两个实例数组中。到这里所有要暴露给JS的类和方法都被装到了RCTBatchedBridge的实例容器中。这里我要补充下,ModuleData里面有一个requiresMainQueueSetup表明当前模块是否在主线程执行,这样就避免了UI事件在子线程中执行。
moduleData准备完毕之后,还会做一些初始化js执行器等等操作,并且把配置表写入JS的操作,这里用到了dispatch_group来控制当javascriptExcutor初始化完毕,配置表加载完毕之后再执行JS代码
//接上文initModulesWithDispatchGroup方法
__block NSString *config;
dispatch_group_enter(initModulesAndLoadSource);
dispatch_async(bridgeQueue, ^{
dispatch_group_t setupJSExecutorAndModuleConfig = dispatch_group_create();
// Asynchronously initialize the JS executor
dispatch_group_async(setupJSExecutorAndModuleConfig, bridgeQueue, ^{
[performanceLogger markStartForTag:RCTPLJSCExecutorSetup];
[weakSelf setUpExecutor];
[performanceLogger markStopForTag:RCTPLJSCExecutorSetup];
});
// Asynchronously gather the module config
dispatch_group_async(setupJSExecutorAndModuleConfig, bridgeQueue, ^{
if (weakSelf.valid) {
RCT_PROFILE_BEGIN_EVENT(0, @"-[RCTBatchedBridge moduleConfig", nil);
[performanceLogger markStartForTag:RCTPLNativeModulePrepareConfig];
config = [weakSelf moduleConfig];
[performanceLogger markStopForTag:RCTPLNativeModulePrepareConfig];
RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
}
});
dispatch_group_notify(setupJSExecutorAndModuleConfig, bridgeQueue, ^{
// We're not waiting for this to complete to leave dispatch group, since
// injectJSONConfiguration and executeSourceCode will schedule operations
// on the same queue anyway.
[performanceLogger markStartForTag:RCTPLNativeModuleInjectConfig];
[weakSelf injectJSONConfiguration:config onComplete:^(NSError *error) {
[performanceLogger markStopForTag:RCTPLNativeModuleInjectConfig];
if (error) {
RCTLogWarn(@"Failed to inject config: %@", error);
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf stopLoadingWithError:error];
});
}
}];
dispatch_group_leave(initModulesAndLoadSource);
});
});
dispatch_group_notify(initModulesAndLoadSource, bridgeQueue, ^{
RCTBatchedBridge *strongSelf = weakSelf;
if (sourceCode && strongSelf.loading) {
[strongSelf executeSourceCode:sourceCode];
}
});
在这里注意三个方法setUpExecutor,moduleConfig和injectJSONConfiguration。
先来说说setUpExecutor,在这里会调到RCTJSCExecutor setUp方法。在setUp主要看调用了executeBlockOnJavaScriptQueue为JS添加了一些全局变量
[self executeBlockOnJavaScriptQueue:^{
if (!self.valid) {
return;
}
JSContext *context = nil;
if (self->_jscWrapper) {
RCTAssert(self->_context != nil, @"If wrapper was pre-initialized, context should be too");
context = self->_context.context;
} else {
[self->_performanceLogger markStartForTag:RCTPLJSCWrapperOpenLibrary];
self->_jscWrapper = RCTJSCWrapperCreate(self->_useCustomJSCLibrary);
[self->_performanceLogger markStopForTag:RCTPLJSCWrapperOpenLibrary];
RCTAssert(self->_context == nil, @"Didn't expect to set up twice");
context = [self->_jscWrapper->JSContext new];
self->_context = [[RCTJavaScriptContext alloc] initWithJSContext:context onThread:self->_javaScriptThread];
[[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptContextCreatedNotification
object:context];
installBasicSynchronousHooksOnContext(context);
}
NSMutableDictionary *threadDictionary = [[NSThread currentThread] threadDictionary];
if (!threadDictionary[RCTFBJSContextClassKey] || !threadDictionary[RCTFBJSValueClassKey]) {
threadDictionary[RCTFBJSContextClassKey] = self->_jscWrapper->JSContext;
threadDictionary[RCTFBJSValueClassKey] = self->_jscWrapper->JSValue;
}
__weak RCTJSCExecutor *weakSelf = self;
context[@"nativeRequireModuleConfig"] = ^NSArray *(NSString *moduleName) {
RCTJSCExecutor *strongSelf = weakSelf;
if (!strongSelf.valid) {
return nil;
}
RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"nativeRequireModuleConfig", @{ @"moduleName": moduleName });
NSArray *result = [strongSelf->_bridge configForModuleName:moduleName];
RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"js_call,config");
return RCTNullIfNil(result);
};
context[@"nativeFlushQueueImmediate"] = ^(NSArray<NSArray *> *calls){
RCTJSCExecutor *strongSelf = weakSelf;
if (!strongSelf.valid || !calls) {
return;
}
RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"nativeFlushQueueImmediate", nil);
[strongSelf->_bridge handleBuffer:calls batchEnded:NO];
RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"js_call");
};
这里只说两个重要回调,一个是nativeRequireModuleConfig,一个是nativeFlushQueueImmediate。那么这两个都有什么作用呢?先说nativeRequireModuleConfig,后面我们会知道,其实RN并没有保存整个的OC方法配置表,而仅仅保存了模块的名字。这个回调就是JS每次根据类名可以查到这个类的模块配置信息。
然后就是nativeFlushQueueImmediate,一般来说JS并不会主动调用OC的方法,而是等着OC定时器每隔一段时间到JS的eventQueue中去取,取出来以后批量执行。而nativeFlushQueueImmediate就是让JS直接调用OC的方法而不用等待。
然后我们可以看到所有的调用都是handleBuffer这个方法来处理
[strongSelf->_bridge handleBuffer:calls batchEnded:NO];
calls的话是一个数组,数组的格式我们可以打印出来看一下:
<__NSArrayM 0x28362e610>(
<__NSArrayM 0x28362d350>(
50
)
,
<__NSArrayM 0x28362e340>(
5
)
,
<__NSArrayM 0x28362d740>(
<__NSArrayM 0x28362cfc0>(
4,
RCTView,
1,
{
flex = 1;
}
)
)
,
2
)
这是一个数组,数组的一个元素是模块ID列表,第二个参数是方法ID列表,第三个就是参数列表。
然后就是moduleConfig和injectJSONConfiguration:
- (NSString *)moduleConfig
{
NSMutableArray<NSArray *> *config = [NSMutableArray new];
for (RCTModuleData *moduleData in _moduleDataByID) {
if (self.executorClass == [RCTJSCExecutor class]) {
[config addObject:@[moduleData.name]];
} else {
[config addObject:RCTNullIfNil(moduleData.config)];
}
}
return RCTJSONStringify(@{
@"remoteModuleConfig": config,
}, NULL);
}
在之前也介绍过:moduleConfig把所有暴露给JS方法放到一个以remoteModuleConfig为key的字典里并序列化。
injectJSONConfiguration会把序列化好的字符串赋值给JS上下文的全局变量__fbBatchedBridgeConfig。
这样JS里面就保存了所有OC暴露的方法名称。
接下来我们就开始执行JS代码
dispatch_group_notify(initModulesAndLoadSource, bridgeQueue, ^{
RCTBatchedBridge *strongSelf = weakSelf;
if (sourceCode && strongSelf.loading) {
[strongSelf executeSourceCode:sourceCode];
}
});
二.RN执行过程
我们发现真正执行代码的是RCTJSExecutor的executeApplicationScript函数。
static NSError *executeApplicationScript(NSData *script, NSURL *sourceURL, RCTJSCWrapper *jscWrapper,
RCTPerformanceLogger *performanceLogger, JSGlobalContextRef ctx)
{
RCT_PROFILE_BEGIN_EVENT(0, @"executeApplicationScript / execute script", (@{
@"url": sourceURL.absoluteString, @"size": @(script.length)
}));
[performanceLogger markStartForTag:RCTPLScriptExecution];
JSValueRef jsError = NULL;
JSStringRef execJSString = jscWrapper->JSStringCreateWithUTF8CString((const char *)script.bytes);
JSStringRef bundleURL = jscWrapper->JSStringCreateWithUTF8CString(sourceURL.absoluteString.UTF8String);
jscWrapper->JSEvaluateScript(ctx, execJSString, NULL, bundleURL, 0, &jsError);
jscWrapper->JSStringRelease(bundleURL);
jscWrapper->JSStringRelease(execJSString);
[performanceLogger markStopForTag:RCTPLScriptExecution];
NSError *error = jsError ? RCTNSErrorFromJSErrorRef(jsError, ctx, jscWrapper) : nil;
RCT_PROFILE_END_EVENT(0, @"js_call");
return error;
}
通过代码可以推测,真正的执行者应该是JSCWrapper->JSEvaluateScript。在这里执行完毕之后,所有需要调用OC的事件都会被放到JS的eventQueue中。我们可以发现最后会回调到RCTJSCExecutor的flushedQueue方法。这个方法最后又会调到_executeJSCall,这是OC调用JS所执行的方法。
- (void)_executeJSCall:(NSString *)method
arguments:(NSArray *)arguments
unwrapResult:(BOOL)unwrapResult
callback:(RCTJavaScriptCallback)onComplete
{
RCTAssert(onComplete != nil, @"onComplete block should not be nil");
__weak RCTJSCExecutor *weakSelf = self;
[self executeBlockOnJavaScriptQueue:^{
RCTJSCExecutor *strongSelf = weakSelf;
if (!strongSelf || !strongSelf.isValid) {
return;
}
RCT_PROFILE_BEGIN_EVENT(0, @"executeJSCall", (@{@"method": method, @"args": arguments}));
RCTJSCWrapper *jscWrapper = strongSelf->_jscWrapper;
JSContext *context = strongSelf->_context.context;
JSGlobalContextRef contextJSRef = context.JSGlobalContextRef;
// get the BatchedBridge object
JSValueRef errorJSRef = NULL;
JSValueRef batchedBridgeRef = strongSelf->_batchedBridgeRef;
if (!batchedBridgeRef) {
JSStringRef moduleNameJSStringRef = jscWrapper->JSStringCreateWithUTF8CString("__fbBatchedBridge");
JSObjectRef globalObjectJSRef = jscWrapper->JSContextGetGlobalObject(contextJSRef);
batchedBridgeRef = jscWrapper->JSObjectGetProperty(contextJSRef, globalObjectJSRef, moduleNameJSStringRef, &errorJSRef);
jscWrapper->JSStringRelease(moduleNameJSStringRef);
strongSelf->_batchedBridgeRef = batchedBridgeRef;
}
NSError *error;
JSValueRef resultJSRef = NULL;
if (batchedBridgeRef != NULL && errorJSRef == NULL && !jscWrapper->JSValueIsUndefined(contextJSRef, batchedBridgeRef)) {
// get method
JSStringRef methodNameJSStringRef = jscWrapper->JSStringCreateWithCFString((__bridge CFStringRef)method);
JSValueRef methodJSRef = jscWrapper->JSObjectGetProperty(contextJSRef, (JSObjectRef)batchedBridgeRef, methodNameJSStringRef, &errorJSRef);
jscWrapper->JSStringRelease(methodNameJSStringRef);
if (methodJSRef != NULL && errorJSRef == NULL && !jscWrapper->JSValueIsUndefined(contextJSRef, methodJSRef)) {
JSValueRef jsArgs[arguments.count];
for (NSUInteger i = 0; i < arguments.count; i++) {
jsArgs[i] = [jscWrapper->JSValue valueWithObject:arguments[i] inContext:context].JSValueRef;
}
resultJSRef = jscWrapper->JSObjectCallAsFunction(contextJSRef, (JSObjectRef)methodJSRef, (JSObjectRef)batchedBridgeRef, arguments.count, jsArgs, &errorJSRef);
} else {
if (!errorJSRef && jscWrapper->JSValueIsUndefined(contextJSRef, methodJSRef)) {
error = RCTErrorWithMessage([NSString stringWithFormat:@"Unable to execute JS call: method %@ is undefined", method]);
}
}
} else {
if (!errorJSRef && jscWrapper->JSValueIsUndefined(contextJSRef, batchedBridgeRef)) {
error = RCTErrorWithMessage(@"Unable to execute JS call: __fbBatchedBridge is undefined");
}
}
id objcValue;
if (errorJSRef || error) {
if (!error) {
error = RCTNSErrorFromJSError([jscWrapper->JSValue valueWithJSValueRef:errorJSRef inContext:context]);
}
} else {
// We often return `null` from JS when there is nothing for native side. [JSValue toValue]
// returns [NSNull null] in this case, which we don't want.
if (!jscWrapper->JSValueIsNull(contextJSRef, resultJSRef)) {
JSValue *result = [jscWrapper->JSValue valueWithJSValueRef:resultJSRef inContext:context];
objcValue = unwrapResult ? [result toObject] : result;
}
}
RCT_PROFILE_END_EVENT(0, @"js_call");
onComplete(objcValue, error);
}];
}
flushedQueue就是从JS的eventQueue中获取任务,具体调用的是callFunctionReturnFlushedQueue这个方法,真正的调用者是_executeJSCall。以上调用只是初始化js的环境,并没有真正执行我们的业务代码。
执行完flushedQueue之后,这里会向主线程发送一个RCTJavaScriptDidLoadNotification通知,这个通知注册在RCTRootView中。接到通知之后,RCTRootView首先会新建一个RCTRootContentView,这个view是所有我们创建的视图的父视图,它会被首先注册在_viewRegistry(RN管理视图的容器)tag为1,目的是表明view的层级是最底层。此外RN还会创建一个RCTRootShadowView以之对应,并且也会被加入一个容器_shadowViewRegistry里。详细代码在RCTUIManager的registerRootView方法。
- (void)registerRootView:(UIView *)rootView withSizeFlexibility:(RCTRootViewSizeFlexibility)sizeFlexibility
{
RCTAssertMainQueue();
NSNumber *reactTag = rootView.reactTag;
RCTAssert(RCTIsReactRootView(reactTag),
@"View %@ with tag #%@ is not a root view", rootView, reactTag);
UIView *existingView = _viewRegistry[reactTag];
RCTAssert(existingView == nil || existingView == rootView,
@"Expect all root views to have unique tag. Added %@ twice", reactTag);
// Register view
_viewRegistry[reactTag] = rootView;
CGRect frame = rootView.frame;
// Register shadow view
dispatch_async(RCTGetUIManagerQueue(), ^{
if (!self->_viewRegistry) {
return;
}
RCTRootShadowView *shadowView = [RCTRootShadowView new];
shadowView.reactTag = reactTag;
shadowView.frame = frame;
shadowView.backgroundColor = rootView.backgroundColor;
shadowView.viewName = NSStringFromClass([rootView class]);
shadowView.sizeFlexibility = sizeFlexibility;
self->_shadowViewRegistry[shadowView.reactTag] = shadowView;
[self->_rootViewTags addObject:reactTag];
});
[[NSNotificationCenter defaultCenter] postNotificationName:RCTUIManagerDidRegisterRootViewNotification
object:self
userInfo:@{RCTUIManagerRootViewKey: rootView}];
}
在容器视图初始化完成之后会发送一个通知,但是目前苹果好像并没有实现这个通知的接收者。
初始化容器视图之后,就会调用 [self runApplication:bridge],这个方法开始执行我们的业务代码。
- (void)runApplication:(RCTBridge *)bridge
{
NSString *moduleName = _moduleName ?: @"";
NSDictionary *appParameters = @{
@"rootTag": _contentView.reactTag,
@"initialProps": _appProperties ?: @{},
};
RCTLogInfo(@"Running application %@ (%@)", moduleName, appParameters);
[bridge enqueueJSCall:@"AppRegistry"
method:@"runApplication"
args:@[moduleName, appParameters]
completion:NULL];
}
这里其实就会调用到RCTBatchedBridge的enqueueJSCall方法。内部的话先会调用_actuallyInvokeAndProcessModule,这个方法会真正调用刚才我们所说的_executeJSCall方法,然后在这里设置了一个回调,当js执行结束后会回调_processResponse方法并且把得到的调用用数组传进去。
- (void)_actuallyInvokeAndProcessModule:(NSString *)module
method:(NSString *)method
arguments:(NSArray *)args
{
RCTAssertJSThread();
__weak __typeof(self) weakSelf = self;
[_javaScriptExecutor callFunctionOnModule:module
method:method
arguments:args
callback:^(id json, NSError *error) {
//这里得到的json的格式我们之前有列出来
[weakSelf _processResponse:json error:error];
}];
}
这里就会把json(需要调用的原生方法数组)里面的moduleId,methodId,args取出来去调用callNativeModule。
[self callNativeModule:[moduleIDs[index] integerValue]
method:[methodIDs[index] integerValue]
params:paramsArrays[index]];
callNativeModule通过初始化时候创建的全局模块信息,取出对象的类名,方法名,生成NSInvoke对象去调用相应的方法。这里重点说一下UI对象。如果说是要创建UI对象的话,RN是直接调到UIManager createView方法(要注意这个方法是JS来调用的)。
createView:(nonnull NSNumber *)reactTag
viewName:(NSString *)viewName
rootTag:(__unused NSNumber *)rootTag
props:(NSDictionary *)props
这个方法里会首先根据全局字典获得一个RCTComponentData,根据reactTag创建一个RCTShadowView并且把RCTShadowView和props(view的布局)关联在RCTComponentData里。对View也是一样的操作,并且会把View放到_bridgeTransactionListeners这个容器中。这个容器是干嘛的呢?稍后会介绍。
执行完了createView方法,接下来会干什么?这里要注意,创建完了View有可能View里面还包含有子View。所以RN在这里判断:如果有当前的View有子View,会调用setChildren方法。
RCT_EXPORT_METHOD(setChildren:(nonnull NSNumber *)containerTag
reactTags:(NSArray<NSNumber *> *)reactTags)
{
//_shadowViewRegistry已经在createView的时候把子View添加进去了
RCTSetChildren(containerTag, reactTags,
(NSDictionary<NSNumber *, id<RCTComponent>> *)_shadowViewRegistry);
[self addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry){
RCTSetChildren(containerTag, reactTags,
(NSDictionary<NSNumber *, id<RCTComponent>> *)viewRegistry);
}];
}
在这里要注意RCTSetChildren的操作。这里对于RCTShadowView的设置操作并不是真正的在View上做插入操作,而是放到内部的一个全局数组容器中。而真正的操作被放入了UIBlock等待执行。
那么在子View也设置完成之后,代码会往哪儿走呢?还记得我们是怎么调到设置子View的方法的吗?是JS直接调用。而JS直接调用一开始会先调到handleBuffer方法,再来回顾一下这个方法。
- (void)handleBuffer:(id)buffer batchEnded:(BOOL)batchEnded
{
RCTAssertJSThread();
if (buffer != nil && buffer != (id)kCFNull) {
_wasBatchActive = YES;
[self handleBuffer:buffer];
[self partialBatchDidFlush];
}
if (batchEnded) {
if (_wasBatchActive) {
[self batchDidComplete];
}
_wasBatchActive = NO;
}
}
刚才我们只是调用完了[self handleBuffer:buffer]来处理JS调用,在这个调用结束后,还有一个[self batchDidComplete]操作,我们来看看这个方法具体做了什么:
- (void)batchDidComplete
{
// TODO: batchDidComplete is only used by RCTUIManager - can we eliminate this special case?
for (RCTModuleData *moduleData in _moduleDataByID) {
if (moduleData.hasInstance && moduleData.implementsBatchDidComplete) {
[self dispatchBlock:^{
[moduleData.instance batchDidComplete];
} queue:moduleData.methodQueue];
}
}
}
在每一个moduleData完成调用之后,会被标记为implementsBatchDidComplete。这里会拿到已经完成的moduleData. instance去调用batchDidComplete。还记得我们刚刚执行的是哪一个方法么?刚刚我们执行完成的是createView,createView是RCTUIManager的方法,所以这里的[moduleData.instance batchDidComplete]就会调到RCTUIManager的batchDidComplete。这个方法内部会调用_layoutAndMount,在这个方法内部会执行刚才创建的UIBlock进行UI布局。
接下来我们看看具体是怎么布局的:
- (void)_layoutAndMount
{
// Gather blocks to be executed now that all view hierarchy manipulations have
// been completed (note that these may still take place before layout has finished)
for (RCTComponentData *componentData in _componentDataByName.allValues) {
RCTViewManagerUIBlock uiBlock = [componentData uiBlockToAmendWithShadowViewRegistry:_shadowViewRegistry];
[self addUIBlock:uiBlock];
}
// Perform layout
for (NSNumber *reactTag in _rootViewTags) {
RCTRootShadowView *rootView = (RCTRootShadowView *)_shadowViewRegistry[reactTag];
[self addUIBlock:[self uiBlockWithLayoutUpdateForRootView:rootView]];
[self _amendPendingUIBlocksWithStylePropagationUpdateForShadowView:rootView];
}
[self addUIBlock:^(RCTUIManager *uiManager, __unused NSDictionary<NSNumber *, UIView *> *viewRegistry) {
/**
* TODO(tadeu): Remove it once and for all
*/
for (id<RCTComponent> node in uiManager->_bridgeTransactionListeners) {
[node reactBridgeDidFinishTransaction];
}
}];
[self flushUIBlocks];
}
直接来看第二个for循环,在[self uiBlockWithLayoutUpdateForRootView:rootView]方法中,我们会真正的计算出每一个RCTShadowView的坐标以及其子View的坐标。uiBlockWithLayoutUpdateForRootView的实现比较长,这里就不粘贴了。在这个方法一开始就会去计算所有RCTRootShadowView的坐标。详细的计算在[RCTRootShadowView collectViewsWithUpdatedFrames]。感兴趣的同学可以看看RN是怎么实现的。计算完以后我们就会得到所有RCTRootShadowView的坐标,接下来这个方法会返回一个Block,这个Block里面会找到每一个RCTRootShadowView对应的RCTView然后设置上frame,如果有动画的话还会进行动画的执行。这个Block会被装进UIBlock队列中。
那么真正的subview是怎么添加的父view上的呢?真相就在[self _amendPendingUIBlocksWithStylePropagationUpdateForShadowView:rootView]这个方法调用里。
- (void)_amendPendingUIBlocksWithStylePropagationUpdateForShadowView:(RCTShadowView *)topView
{
NSMutableSet<RCTApplierBlock> *applierBlocks = [NSMutableSet setWithCapacity:1];
[topView collectUpdatedProperties:applierBlocks parentProperties:@{}];
if (applierBlocks.count) {
[self addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
for (RCTApplierBlock block in applierBlocks) {
block(viewRegistry);
}
}];
}
}
首先在这里会创建一个block容器applierBlocks。我们会调用collectUpdatedProperties为容器添加一些block,并最终放到UI队列里面去执行。然后我们来看看collectUpdatedProperties具体做了什么操作:
- (void)collectUpdatedProperties:(NSMutableSet<RCTApplierBlock> *)applierBlocks
parentProperties:(NSDictionary<NSString *, id> *)parentProperties
{
if (_propagationLifecycle == RCTUpdateLifecycleComputed && [parentProperties isEqualToDictionary:_lastParentProperties]) {
return;
}
_propagationLifecycle = RCTUpdateLifecycleComputed;
_lastParentProperties = parentProperties;
NSDictionary<NSString *, id> *nextProps = [self processUpdatedProperties:applierBlocks parentProperties:parentProperties];
for (RCTShadowView *child in _reactSubviews) {
[child collectUpdatedProperties:applierBlocks parentProperties:nextProps];
}
}
这里主要看[self processUpdatedProperties:applierBlocks parentProperties:parentProperties]这个方法。
- (NSDictionary<NSString *, id> *)processUpdatedProperties:(NSMutableSet<RCTApplierBlock> *)applierBlocks
parentProperties:(NSDictionary<NSString *, id> *)parentProperties
{
// TODO: we always refresh all propagated properties when propagation is
// dirtied, but really we should track which properties have changed and
// only update those.
if (_didUpdateSubviews) {
_didUpdateSubviews = NO;
[self didUpdateReactSubviews];
[applierBlocks addObject:^(NSDictionary<NSNumber *, UIView *> *viewRegistry) {
UIView *view = viewRegistry[self->_reactTag];
[view clearSortedSubviews];
[view didUpdateReactSubviews];
}];
}
if (!_backgroundColor) {
UIColor *parentBackgroundColor = parentProperties[RCTBackgroundColorProp];
if (parentBackgroundColor) {
[applierBlocks addObject:^(NSDictionary<NSNumber *, UIView *> *viewRegistry) {
UIView *view = viewRegistry[self->_reactTag];
[view reactSetInheritedBackgroundColor:parentBackgroundColor];
}];
}
} else {
// Update parent properties for children
NSMutableDictionary<NSString *, id> *properties = [NSMutableDictionary dictionaryWithDictionary:parentProperties];
CGFloat alpha = CGColorGetAlpha(_backgroundColor.CGColor);
if (alpha < 1.0) {
// If bg is non-opaque, don't propagate further
properties[RCTBackgroundColorProp] = [UIColor clearColor];
} else {
properties[RCTBackgroundColorProp] = _backgroundColor;
}
return properties;
}
return parentProperties;
}
主要就看第一个if语句。这里会往applierBlocks添加一个block,这个block其实就会把当前RCTShadowView相关联的RCTView取出来,然后拿到相对应的子View。我们主要看didUpdateReactSubviews这个方法。执行这个方法其实会走到UIView的扩展didUpdateReactSubviews方法里。
- (void)didUpdateReactSubviews
{
for (UIView *subview in self.sortedReactSubviews) {
[self addSubview:subview];
}
}
拿到子View的关键就在这个sortedReactSubviews。通过函数调用链可以看到其实我们最终拿到子View是在
- (NSArray<UIView *> *)reactSubviews
{
return objc_getAssociatedObject(self, _cmd);
}
方法里,然后我们看下这个关联对象的设置位置:
- (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)atIndex
{
// We access the associated object directly here in case someone overrides
// the `reactSubviews` getter method and returns an immutable array.
NSMutableArray *subviews = objc_getAssociatedObject(self, @selector(reactSubviews));
if (!subviews) {
subviews = [NSMutableArray new];
objc_setAssociatedObject(self, @selector(reactSubviews), subviews, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
[subviews insertObject:subview atIndex:atIndex];
}
同样是在该扩展的insertReactSubview方法。这个方法是不是很熟悉?还记得我们之前所说的JS调用完RCTUIManager的createView就会去调setChildren吗?没错就是在setChildren的时候我们会调用到这里为UIView扩展的关联对象添加子View。
然后要记得所有的任务其实都是被放到了UI队列里面去做的,所以在最后的话其实是会执行UI队列里的操作。
网友评论