[ReactNative] Parellelise bridge startup

Summary:
Parallelise the bridge startup so we don't keep waiting for the source to load.
This commit is contained in:
Tadeu Zagallo 2015-08-07 06:42:34 -07:00
parent 3cef3010e6
commit 6cd0709bc0
8 changed files with 315 additions and 235 deletions

View File

@ -103,10 +103,6 @@ RCT_EXPORT_METHOD(test:(__unused NSString *)a
(void)bridge;
}
/**
* Sleep on the main thread to allow js thread deallocations then run the runloop
* to allow the module to be deallocated on the main thread
*/
RUN_RUNLOOP_WHILE(module.isValid)
XCTAssertFalse(module.isValid, @"AllocationTestModule should have been invalidated by the bridge");
}

View File

@ -120,10 +120,22 @@ RCT_EXPORT_MODULE(TestModule)
[_bridge invalidate];
}
#define RUN_RUNLOOP_WHILE(CONDITION) \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wshadow\"") \
NSDate *timeout = [[NSDate date] dateByAddingTimeInterval:0.1]; \
while ((CONDITION) && [timeout timeIntervalSinceNow] > 0) { \
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:timeout]; \
} \
_Pragma("clang diagnostic pop")
- (void)testHookRegistration
{
TestExecutor *executor = [_bridge.batchedBridge valueForKey:@"_javaScriptExecutor"];
NSString *injectedStuff = executor.injectedStuff[@"__fbBatchedBridgeConfig"];
NSString *injectedStuff;
RUN_RUNLOOP_WHILE(!(injectedStuff = executor.injectedStuff[@"__fbBatchedBridgeConfig"]));
NSDictionary *moduleConfig = RCTJSONParse(injectedStuff, NULL);
NSDictionary *remoteModuleConfig = moduleConfig[@"remoteModuleConfig"];
NSDictionary *testModuleConfig = remoteModuleConfig[@"TestModule"];
@ -142,7 +154,10 @@ RCT_EXPORT_MODULE(TestModule)
- (void)testCallNativeMethod
{
TestExecutor *executor = [_bridge.batchedBridge valueForKey:@"_javaScriptExecutor"];
NSString *injectedStuff = executor.injectedStuff[@"__fbBatchedBridgeConfig"];
NSString *injectedStuff;
RUN_RUNLOOP_WHILE(!(injectedStuff = executor.injectedStuff[@"__fbBatchedBridgeConfig"]));
NSDictionary *moduleConfig = RCTJSONParse(injectedStuff, NULL);
NSDictionary *remoteModuleConfig = moduleConfig[@"remoteModuleConfig"];
NSDictionary *testModuleConfig = remoteModuleConfig[@"TestModule"];

View File

@ -70,6 +70,7 @@ id<RCTJavaScriptExecutor> RCTGetLatestExecutor(void)
NSMutableSet *_frameUpdateObservers;
NSMutableArray *_scheduledCalls;
RCTSparseArray *_scheduledCallbacks;
NSURL *_sourceURL;
}
@synthesize valid = _valid;
@ -101,77 +102,119 @@ id<RCTJavaScriptExecutor> RCTGetLatestExecutor(void)
[_mainDisplayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
}
/**
* Initialize and register bridge modules *before* adding the display link
* so we don't have threading issues
*/
[self registerModules];
/**
* If currently profiling, hook into the current instance
*/
if (RCTProfileIsProfiling()) {
RCTProfileHookModules(self);
}
[[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptWillStartLoadingNotification
object:self
userInfo:@{ @"bridge": self }];
/**
* Start the application script
*/
[self initJS];
[self start];
}
return self;
}
RCT_NOT_IMPLEMENTED(-initWithBundleURL:(__unused NSURL *)bundleURL
moduleProvider:(__unused RCTBridgeModuleProviderBlock)block
launchOptions:(__unused NSDictionary *)launchOptions)
- (void)setUp {}
- (void)bindKeys {}
- (void)reload
- (void)start
{
[_parentBridge reload];
_sourceURL = self.delegate ? [self.delegate sourceURLForBridge:_parentBridge] : self.bundleURL;
__weak RCTBatchedBridge *weakSelf = self;
__block NSString *sourceCode;
__block NSString *config;
dispatch_queue_t bridgeQueue = dispatch_queue_create("com.facebook.react.RCTBridgeQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t initModulesAndLoadSource = dispatch_group_create();
dispatch_group_enter(initModulesAndLoadSource);
[weakSelf loadSource:^(NSError *error, NSString *source) {
if (error) {
RCTLogError(@"%@", error);
} else {
sourceCode = source;
}
dispatch_group_leave(initModulesAndLoadSource);
}];
[self initModules];
dispatch_group_enter(initModulesAndLoadSource);
dispatch_async(bridgeQueue, ^{
dispatch_group_t setupJSExecutorAndModuleConfig = dispatch_group_create();
dispatch_group_async(setupJSExecutorAndModuleConfig, bridgeQueue, ^{
[weakSelf setupExecutor];
});
dispatch_group_async(setupJSExecutorAndModuleConfig, bridgeQueue, ^{
if (weakSelf.isValid) {
config = [weakSelf moduleConfig];
if (RCTProfileIsProfiling()) {
RCTProfileHookModules(weakSelf);
}
}
});
dispatch_group_notify(setupJSExecutorAndModuleConfig, bridgeQueue, ^{
[weakSelf injectJSONConfiguration:config onComplete:^(__unused NSError *error) {}];
dispatch_group_leave(initModulesAndLoadSource);
});
});
dispatch_group_notify(initModulesAndLoadSource, bridgeQueue, ^{
if (sourceCode) {
[weakSelf executeSourceCode:sourceCode];
}
});
}
- (Class)executorClass
- (void)loadSource:(RCTSourceLoadBlock)_onSourceLoad
{
return _parentBridge.executorClass ?: [RCTContextExecutor class];
RCTPerformanceLoggerStart(RCTPLScriptDownload);
RCTProfileBeginEvent();
RCTSourceLoadBlock onSourceLoad = ^(NSError *error, NSString *source) {
RCTPerformanceLoggerEnd(RCTPLScriptDownload);
RCTProfileEndEvent(@"JavaScript download", @"init,download", @[]);
if (error) {
NSArray *stack = [error userInfo][@"stack"];
if (stack) {
[[RCTRedBox sharedInstance] showErrorMessage:[error localizedDescription]
withStack:stack];
} else {
[[RCTRedBox sharedInstance] showErrorMessage:[error localizedDescription]
withDetails:[error localizedFailureReason]];
}
NSDictionary *userInfo = @{@"bridge": self, @"error": error};
[[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidFailToLoadNotification
object:_parentBridge
userInfo:userInfo];
}
_onSourceLoad(error, source);
};
if ([self.delegate respondsToSelector:@selector(loadSourceForBridge:withBlock:)]) {
[self.delegate loadSourceForBridge:_parentBridge withBlock:onSourceLoad];
} else if (_sourceURL) {
[RCTJavaScriptLoader loadBundleAtURL:_sourceURL
onComplete:onSourceLoad];
} else {
// Allow testing without a script
dispatch_async(dispatch_get_main_queue(), ^{
_loading = NO;
[[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidLoadNotification
object:_parentBridge
userInfo:@{ @"bridge": self }];
});
onSourceLoad(nil, nil);
}
}
- (void)setExecutorClass:(Class)executorClass
{
RCTAssertMainThread();
_parentBridge.executorClass = executorClass;
}
- (NSURL *)bundleURL
{
return _parentBridge.bundleURL;
}
- (void)setBundleURL:(NSURL *)bundleURL
{
_parentBridge.bundleURL = bundleURL;
}
- (id<RCTBridgeDelegate>)delegate
{
return _parentBridge.delegate;
}
- (BOOL)isLoading
{
return _loading;
}
- (BOOL)isValid
{
return _valid;
}
- (void)registerModules
- (void)initModules
{
RCTAssertMainThread();
@ -234,9 +277,6 @@ RCT_NOT_IMPLEMENTED(-initWithBundleURL:(__unused NSURL *)bundleURL
_javaScriptExecutor = _modulesByName[RCTBridgeModuleNameForClass(self.executorClass)];
RCTLatestExecutor = _javaScriptExecutor;
[_javaScriptExecutor setUp];
// Set bridge
for (id<RCTBridgeModule> module in _modulesByName.allValues) {
if ([module respondsToSelector:@selector(setBridge:)]) {
module.bridge = self;
@ -246,127 +286,144 @@ RCT_NOT_IMPLEMENTED(-initWithBundleURL:(__unused NSURL *)bundleURL
uid:@(_moduleDataByID.count)
instance:module];
[_moduleDataByID addObject:moduleData];
if ([module conformsToProtocol:@protocol(RCTFrameUpdateObserver)]) {
[_frameUpdateObservers addObject:moduleData];
}
}
[[NSNotificationCenter defaultCenter] postNotificationName:RCTDidCreateNativeModules
object:self];
}
- (void)initJS
{
RCTAssertMainThread();
// Inject module data into JS context
- (void)setupExecutor
{
[_javaScriptExecutor setUp];
}
- (NSString *)moduleConfig
{
NSMutableDictionary *config = [[NSMutableDictionary alloc] init];
for (RCTModuleData *moduleData in _moduleDataByID) {
config[moduleData.name] = moduleData.config;
if ([moduleData.instance conformsToProtocol:@protocol(RCTFrameUpdateObserver)]) {
[_frameUpdateObservers addObject:moduleData];
}
}
NSString *configJSON = RCTJSONStringify(@{
return RCTJSONStringify(@{
@"remoteModuleConfig": config,
}, NULL);
}
- (void)injectJSONConfiguration:(NSString *)configJSON
onComplete:(void (^)(NSError *))onComplete
{
if (!self.isValid) {
return;
}
[_javaScriptExecutor injectJSONText:configJSON
asGlobalObjectNamed:@"__fbBatchedBridgeConfig"
callback:^(NSError *error) {
if (error) {
[[RCTRedBox sharedInstance] showError:error];
}
onComplete(error);
}];
}
if (_javaScriptExecutor == nil) {
- (void)executeSourceCode:(NSString *)sourceCode
{
_loading = NO;
if (!self.isValid || !_javaScriptExecutor) {
return;
}
RCTSourceCode *sourceCodeModule = self.modules[RCTBridgeModuleNameForClass([RCTSourceCode class])];
sourceCodeModule.scriptURL = _sourceURL;
sourceCodeModule.scriptText = sourceCode;
static BOOL shouldDismiss = NO;
if (shouldDismiss) {
[[RCTRedBox sharedInstance] dismiss];
}
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
shouldDismiss = YES;
});
[self enqueueApplicationScript:sourceCode url:_sourceURL onComplete:^(NSError *loadError) {
if (loadError) {
[[RCTRedBox sharedInstance] showError:loadError];
return;
}
/**
* HACK (tadeu): If it failed to connect to the debugger, set loading to NO
* so we can attempt to reload again.
* Register the display link to start sending js calls after everything
* is setup
*/
_loading = NO;
NSRunLoop *targetRunLoop = [_javaScriptExecutor isKindOfClass:[RCTContextExecutor class]] ? [NSRunLoop currentRunLoop] : [NSRunLoop mainRunLoop];
[_jsDisplayLink addToRunLoop:targetRunLoop forMode:NSRunLoopCommonModes];
} else {
[[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptWillStartLoadingNotification
object:self
[[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidLoadNotification
object:_parentBridge
userInfo:@{ @"bridge": self }];
RCTPerformanceLoggerStart(RCTPLScriptDownload);
RCTProfileBeginEvent();
NSURL *sourceURL = self.delegate ? [self.delegate sourceURLForBridge:_parentBridge] : self.bundleURL;
void (^loadCallback)(NSError *, NSString *) = ^(NSError *error, NSString *script) {
RCTPerformanceLoggerEnd(RCTPLScriptDownload);
RCTProfileEndEvent(@"JavaScript download", @"init,download", @[]);
}];
}
_loading = NO;
if (!self.isValid) {
return;
}
static BOOL shouldDismiss = NO;
if (shouldDismiss) {
[[RCTRedBox sharedInstance] dismiss];
}
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
shouldDismiss = YES;
});
RCT_NOT_IMPLEMENTED(-initWithBundleURL:(__unused NSURL *)bundleURL
moduleProvider:(__unused RCTBridgeModuleProviderBlock)block
launchOptions:(__unused NSDictionary *)launchOptions)
RCTSourceCode *sourceCodeModule = self.modules[RCTBridgeModuleNameForClass([RCTSourceCode class])];
sourceCodeModule.scriptURL = sourceURL;
sourceCodeModule.scriptText = script;
if (error) {
/**
* Prevent super from calling setUp (that'd create another batchedBridge)
*/
- (void)setUp {}
- (void)bindKeys {}
NSArray *stack = [error userInfo][@"stack"];
if (stack) {
[[RCTRedBox sharedInstance] showErrorMessage:[error localizedDescription]
withStack:stack];
} else {
[[RCTRedBox sharedInstance] showErrorMessage:[error localizedDescription]
withDetails:[error localizedFailureReason]];
}
- (void)reload
{
[_parentBridge reload];
}
NSDictionary *userInfo = @{@"bridge": self, @"error": error};
[[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidFailToLoadNotification
object:_parentBridge
userInfo:userInfo];
- (Class)executorClass
{
return _parentBridge.executorClass ?: [RCTContextExecutor class];
}
} else {
- (void)setExecutorClass:(Class)executorClass
{
RCTAssertMainThread();
[self enqueueApplicationScript:script url:sourceURL onComplete:^(NSError *loadError) {
_parentBridge.executorClass = executorClass;
}
if (loadError) {
[[RCTRedBox sharedInstance] showError:loadError];
return;
}
- (NSURL *)bundleURL
{
return _parentBridge.bundleURL;
}
/**
* Register the display link to start sending js calls after everything
* is setup
*/
NSRunLoop *targetRunLoop = [_javaScriptExecutor isKindOfClass:[RCTContextExecutor class]] ? [NSRunLoop currentRunLoop] : [NSRunLoop mainRunLoop];
[_jsDisplayLink addToRunLoop:targetRunLoop forMode:NSRunLoopCommonModes];
- (void)setBundleURL:(NSURL *)bundleURL
{
_parentBridge.bundleURL = bundleURL;
}
[[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidLoadNotification
object:_parentBridge
userInfo:@{ @"bridge": self }];
}];
}
};
- (id<RCTBridgeDelegate>)delegate
{
return _parentBridge.delegate;
}
if ([self.delegate respondsToSelector:@selector(loadSourceForBridge:withBlock:)]) {
[self.delegate loadSourceForBridge:_parentBridge withBlock:loadCallback];
} else if (sourceURL) {
[RCTJavaScriptLoader loadBundleAtURL:sourceURL
onComplete:loadCallback];
} else {
// Allow testing without a script
dispatch_async(dispatch_get_main_queue(), ^{
_loading = NO;
[[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidLoadNotification
object:_parentBridge
userInfo:@{ @"bridge": self }];
});
}
}
- (BOOL)isLoading
{
return _loading;
}
- (BOOL)isValid
{
return _valid;
}
- (NSDictionary *)modules
@ -751,14 +808,14 @@ RCT_NOT_IMPLEMENTED(-initWithBundleURL:(__unused NSURL *)bundleURL
RCTProfileImmediateEvent(@"VSYNC", displayLink.timestamp, @"g");
[self.perfStats.uiGraph onTick:displayLink.timestamp];
_modulesByName == nil ?: [self.perfStats.uiGraph onTick:displayLink.timestamp];
}
- (void)startProfiling
{
RCTAssertMainThread();
if (![_parentBridge.bundleURL.scheme isEqualToString:@"http"]) {
if (![_sourceURL.scheme isEqualToString:@"http"]) {
RCTLogError(@"To run the profiler you must be running from the dev server");
return;
}
@ -774,10 +831,8 @@ RCT_NOT_IMPLEMENTED(-initWithBundleURL:(__unused NSURL *)bundleURL
[_javaScriptExecutor executeBlockOnJavaScriptQueue:^{
NSString *log = RCTProfileEnd(self);
NSURL *bundleURL = _parentBridge.bundleURL;
NSString *port = bundleURL.port ? [@":" stringByAppendingString:bundleURL.port.stringValue] : @"";
NSString *URLString = [NSString stringWithFormat:@"%@://%@%@/profile", bundleURL.scheme, bundleURL.host, port];
NSURL *URL = [RCTConvert NSURL:URLString];
NSString *URLString = [NSString stringWithFormat:@"%@://%@:%@/profile", _sourceURL.scheme, _sourceURL.host, _sourceURL.port];
NSURL *URL = [NSURL URLWithString:URLString];
NSMutableURLRequest *URLRequest = [NSMutableURLRequest requestWithURL:URL];
URLRequest.HTTPMethod = @"POST";
[URLRequest setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];

View File

@ -208,7 +208,7 @@ void _RCTLogFormat(
#if RCT_DEBUG // Red box is only available in debug mode
// Log to red box
if (level >= RCTLOG_REDBOX_LEVEL) {
if ([UIApplication sharedApplication] && level >= RCTLOG_REDBOX_LEVEL) {
NSArray *stackSymbols = [NSThread callStackSymbols];
NSMutableArray *stack = [NSMutableArray arrayWithCapacity:(stackSymbols.count - 1)];
[stackSymbols enumerateObjectsUsingBlock:^(NSString *frameSymbols, NSUInteger idx, __unused BOOL *stop) {

View File

@ -14,6 +14,12 @@
#import "RCTLog.h"
@implementation RCTModuleData
{
id _consts;
NSDictionary *_config;
NSArray *_methods;
}
- (instancetype)initWithExecutor:(id<RCTJavaScriptExecutor>)javaScriptExecutor
uid:(NSNumber *)uid
@ -26,102 +32,109 @@
_moduleClass = [instance class];
_name = RCTBridgeModuleNameForClass(_moduleClass);
[self loadMethods];
[self generateConfig];
[self setQueue];
if ([_instance respondsToSelector:@selector(constantsToExport)]) {
_consts = [_instance constantsToExport];
}
}
return self;
}
RCT_NOT_IMPLEMENTED(-init);
- (void)loadMethods
- (NSArray *)methods
{
NSMutableArray *moduleMethods = [[NSMutableArray alloc] init];
unsigned int methodCount;
Method *methods = class_copyMethodList(object_getClass(_moduleClass), &methodCount);
if (!_methods) {
NSMutableArray *moduleMethods = [[NSMutableArray alloc] init];
unsigned int methodCount;
Method *methods = class_copyMethodList(object_getClass(_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);
RCTModuleMethod *moduleMethod =
[[RCTModuleMethod alloc] initWithObjCMethodName:entries[1]
JSMethodName:entries[0]
moduleClass:_moduleClass];
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);
RCTModuleMethod *moduleMethod =
[[RCTModuleMethod alloc] initWithObjCMethodName:entries[1]
JSMethodName:entries[0]
moduleClass:_moduleClass];
[moduleMethods addObject:moduleMethod];
[moduleMethods addObject:moduleMethod];
}
}
free(methods);
_methods = [moduleMethods copy];
}
free(methods);
_methods = [moduleMethods copy];
return _methods;
}
- (void)generateConfig
- (NSDictionary *)config
{
NSMutableDictionary *config = [[NSMutableDictionary alloc] init];
config[@"moduleID"] = _uid;
config[@"methods"] = [[NSMutableDictionary alloc] init];
if (!_config) {
NSMutableDictionary *config = [[NSMutableDictionary alloc] init];
config[@"moduleID"] = _uid;
config[@"methods"] = [[NSMutableDictionary alloc] init];
if ([_instance respondsToSelector:@selector(constantsToExport)]) {
id consts = [_instance constantsToExport];
if (consts) {
config[@"constants"] = consts;
if (_consts) {
config[@"constants"] = _consts;
}
[self.methods enumerateObjectsUsingBlock:^(RCTModuleMethod *method, NSUInteger idx, __unused BOOL *stop) {
config[@"methods"][method.JSMethodName] = @{
@"methodID": @(idx),
@"type": method.functionKind == RCTJavaScriptFunctionKindAsync ? @"remoteAsync" : @"remote",
};
}];
_config = [config copy];
}
[_methods enumerateObjectsUsingBlock:^(RCTModuleMethod *method, NSUInteger idx, __unused BOOL *stop) {
config[@"methods"][method.JSMethodName] = @{
@"methodID": @(idx),
@"type": method.functionKind == RCTJavaScriptFunctionKindAsync ? @"remoteAsync" : @"remote",
};
}];
_config = [config copy];
return _config;
}
- (void)setQueue
- (dispatch_queue_t)queue
{
dispatch_queue_t queue = nil;
BOOL implementsMethodQueue = [_instance respondsToSelector:@selector(methodQueue)];
if (implementsMethodQueue) {
queue = [_instance methodQueue];
}
if (!queue) {
// Need to cache queueNames because they aren't retained by dispatch_queue
static NSMutableDictionary *queueNames;
if (!queueNames) {
queueNames = [[NSMutableDictionary alloc] init];
}
NSString *queueName = queueNames[_name];
if (!queueName) {
queueName = [NSString stringWithFormat:@"com.facebook.React.%@Queue", _name];
queueNames[_name] = queueName;
}
// Create new queue
queue = dispatch_queue_create(queueName.UTF8String, DISPATCH_QUEUE_SERIAL);
// assign it to the module
if (!_queue) {
BOOL implementsMethodQueue = [_instance respondsToSelector:@selector(methodQueue)];
if (implementsMethodQueue) {
@try {
[(id)_instance setValue:queue forKey:@"methodQueue"];
_queue = [_instance methodQueue];
}
if (!_queue) {
// Need to cache queueNames because they aren't retained by dispatch_queue
static NSMutableDictionary *queueNames;
if (!queueNames) {
queueNames = [[NSMutableDictionary alloc] init];
}
@catch (NSException *exception) {
RCTLogError(@"%@ is returning nil for it's methodQueue, which is not "
"permitted. You must either return a pre-initialized "
"queue, or @synthesize the methodQueue to let the bridge "
"create a queue for you.", _name);
NSString *queueName = queueNames[_name];
if (!queueName) {
queueName = [NSString stringWithFormat:@"com.facebook.React.%@Queue", _name];
queueNames[_name] = queueName;
}
// Create new queue
_queue = dispatch_queue_create(queueName.UTF8String, DISPATCH_QUEUE_SERIAL);
// assign it to the module
if (implementsMethodQueue) {
@try {
[(id)_instance setValue:_queue forKey:@"methodQueue"];
}
@catch (NSException *exception) {
RCTLogError(@"%@ is returning nil for it's methodQueue, which is not "
"permitted. You must either return a pre-initialized "
"queue, or @synthesize the methodQueue to let the bridge "
"create a queue for you.", _name);
}
}
}
}
_queue = queue;
return _queue;
}
- (void)dispatchBlock:(dispatch_block_t)block
@ -132,13 +145,13 @@ RCT_NOT_IMPLEMENTED(-init);
- (void)dispatchBlock:(dispatch_block_t)block
dispatchGroup:(dispatch_group_t)group
{
if (_queue == RCTJSThread) {
if (self.queue == RCTJSThread) {
[_javaScriptExecutor executeBlockOnJavaScriptQueue:block];
} else if (_queue) {
} else if (self.queue) {
if (group != NULL) {
dispatch_group_async(group, _queue, block);
dispatch_group_async(group, self.queue, block);
} else {
dispatch_async(_queue, block);
dispatch_async(self.queue, block);
}
}
}

View File

@ -146,7 +146,7 @@ static IMP RCTProfileMsgForward(NSObject *self, SEL selector)
void RCTProfileHookModules(RCTBridge *bridge)
{
for (RCTModuleData *moduleData in [bridge valueForKey:@"_modules"]) {
for (RCTModuleData *moduleData in [bridge valueForKey:@"moduleDataByID"]) {
[moduleData dispatchBlock:^{
Class moduleClass = moduleData.moduleClass;
Class proxyClass = objc_allocateClassPair(moduleClass, RCTProfileProxyClassName(moduleClass), 0);
@ -190,7 +190,7 @@ void RCTProfileHookModules(RCTBridge *bridge)
void RCTProfileUnhookModules(RCTBridge *bridge)
{
for (RCTModuleData *moduleData in [bridge valueForKey:@"_modules"]) {
for (RCTModuleData *moduleData in [bridge valueForKey:@"moduleDataByID"]) {
Class proxyClass = object_getClass(moduleData.instance);
if (moduleData.moduleClass != proxyClass) {
object_setClass(moduleData.instance, moduleData.moduleClass);

View File

@ -37,7 +37,7 @@
{
if ((self = [super initWithFrame:frame])) {
_redColor = [UIColor colorWithRed:0.8 green:0 blue:0 alpha:1];
self.windowLevel = CGFLOAT_MAX;
self.windowLevel = UIWindowLevelAlert + 1000;
self.backgroundColor = _redColor;
self.hidden = YES;

View File

@ -468,7 +468,8 @@ static NSError *RCTNSErrorFromJSError(JSContextRef context, JSValueRef jsError)
sourceURL:(NSURL *)sourceURL
onComplete:(RCTJavaScriptCompleteBlock)onComplete
{
RCTAssert(sourceURL != nil, @"url should not be nil");
RCTAssertParam(script);
RCTAssertParam(sourceURL);
__weak RCTContextExecutor *weakSelf = self;
[self executeBlockOnJavaScriptQueue:RCTProfileBlock((^{