From 5ce9fa4dda79bd925e075afcb552893acdc83628 Mon Sep 17 00:00:00 2001 From: Nick Lockwood Date: Mon, 20 Apr 2015 12:06:02 -0700 Subject: [PATCH] Changed default method queue to a background queue. --- .../ActionSheetIOS/RCTActionSheetManager.m | 5 +++ Libraries/Geolocation/RCTLocationObserver.m | 5 +++ Libraries/LinkingIOS/RCTLinkingManager.m | 13 ++++--- React/Base/RCTBridge.h | 10 +++--- React/Base/RCTBridge.m | 36 ++++++++++--------- React/Base/RCTBridgeModule.h | 26 ++++++++++---- React/Modules/RCTAlertManager.m | 5 +++ React/Modules/RCTStatusBarManager.m | 5 +++ React/Modules/RCTTiming.m | 5 +++ 9 files changed, 78 insertions(+), 32 deletions(-) diff --git a/Libraries/ActionSheetIOS/RCTActionSheetManager.m b/Libraries/ActionSheetIOS/RCTActionSheetManager.m index ac672249f..75798efaf 100644 --- a/Libraries/ActionSheetIOS/RCTActionSheetManager.m +++ b/Libraries/ActionSheetIOS/RCTActionSheetManager.m @@ -30,6 +30,11 @@ RCT_EXPORT_MODULE() return self; } +- (dispatch_queue_t)methodQueue +{ + return dispatch_get_main_queue(); +} + RCT_EXPORT_METHOD(showActionSheetWithOptions:(NSDictionary *)options failureCallback:(RCTResponseSenderBlock)failureCallback successCallback:(RCTResponseSenderBlock)successCallback) diff --git a/Libraries/Geolocation/RCTLocationObserver.m b/Libraries/Geolocation/RCTLocationObserver.m index c5303df15..3e864657b 100644 --- a/Libraries/Geolocation/RCTLocationObserver.m +++ b/Libraries/Geolocation/RCTLocationObserver.m @@ -128,6 +128,11 @@ RCT_EXPORT_MODULE() _locationManager.delegate = nil; } +- (dispatch_queue_t)methodQueue +{ + return dispatch_get_main_queue(); +} + #pragma mark - Private API - (void)beginLocationUpdates diff --git a/Libraries/LinkingIOS/RCTLinkingManager.m b/Libraries/LinkingIOS/RCTLinkingManager.m index 4be8bfb8e..eec17a012 100644 --- a/Libraries/LinkingIOS/RCTLinkingManager.m +++ b/Libraries/LinkingIOS/RCTLinkingManager.m @@ -36,6 +36,11 @@ RCT_EXPORT_MODULE() [[NSNotificationCenter defaultCenter] removeObserver:self]; } +- (dispatch_queue_t)methodQueue +{ + return dispatch_queue_create("com.facebook.React.LinkingManager", DISPATCH_QUEUE_SERIAL); +} + + (BOOL)application:(UIApplication *)application openURL:(NSURL *)URL sourceApplication:(NSString *)sourceApplication @@ -56,16 +61,16 @@ RCT_EXPORT_MODULE() RCT_EXPORT_METHOD(openURL:(NSURL *)URL) { + // Doesn't really matter what thread we call this on since it exits the app [[UIApplication sharedApplication] openURL:URL]; } RCT_EXPORT_METHOD(canOpenURL:(NSURL *)URL callback:(RCTResponseSenderBlock)callback) { - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - BOOL canOpen = [[UIApplication sharedApplication] canOpenURL:URL]; - callback(@[@(canOpen)]); - }); + // This can be expensive, so we deliberately don't call on main thread + BOOL canOpen = [[UIApplication sharedApplication] canOpenURL:URL]; + callback(@[@(canOpen)]); } - (NSDictionary *)constantsToExport diff --git a/React/Base/RCTBridge.h b/React/Base/RCTBridge.h index 544e5e1a2..c0f3a1a91 100644 --- a/React/Base/RCTBridge.h +++ b/React/Base/RCTBridge.h @@ -62,9 +62,9 @@ extern NSString *RCTBridgeModuleNameForClass(Class bridgeModuleClass); /** * This method is used to call functions in the JavaScript application context. * It is primarily intended for use by modules that require two-way communication - * with the JavaScript code. Method should be regsitered using the + * with the JavaScript code. Method should be registered using the * RCT_IMPORT_METHOD macro below. Attempting to call a method that has not been - * registered will result in an error. + * registered will result in an error. Safe to call from any thread. */ - (void)enqueueJSCall:(NSString *)moduleDotMethod args:(NSArray *)args; @@ -112,17 +112,17 @@ static const char *__rct_import_##module##_##method##__ = #module"."#method; @property (nonatomic, readonly, getter=isLoading) BOOL loading; /** - * Reload the bundle and reset executor and modules. + * Reload the bundle and reset executor & modules. Safe to call from any thread. */ - (void)reload; /** - * Add a new observer that will be called on every screen refresh + * Add a new observer that will be called on every screen refresh. */ - (void)addFrameUpdateObserver:(id)observer; /** - * Stop receiving screen refresh updates for the given observer + * Stop receiving screen refresh updates for the given observer. */ - (void)removeFrameUpdateObserver:(id)observer; diff --git a/React/Base/RCTBridge.m b/React/Base/RCTBridge.m index 46dcc1dfe..31a8bea77 100644 --- a/React/Base/RCTBridge.m +++ b/React/Base/RCTBridge.m @@ -228,6 +228,7 @@ static NSArray *RCTBridgeModuleClassesByModuleID(void) NSMethodSignature *_methodSignature; NSArray *_argumentBlocks; NSString *_methodName; + dispatch_block_t _methodQueue; } static Class _globalExecutorClass; @@ -762,6 +763,7 @@ static NSDictionary *RCTLocalModulesConfig() { RCTSparseArray *_modulesByID; RCTSparseArray *_queuesByID; + dispatch_queue_t _methodQueue; NSDictionary *_modulesByName; id _javaScriptExecutor; Class _executorClass; @@ -787,7 +789,6 @@ static id _latestJSExecutor; [self setUp]; [self bindKeys]; } - return self; } @@ -797,6 +798,7 @@ static id _latestJSExecutor; _javaScriptExecutor = RCTCreateExecutor(executorClass); _latestJSExecutor = _javaScriptExecutor; _eventDispatcher = [[RCTEventDispatcher alloc] initWithBridge:self]; + _methodQueue = dispatch_queue_create("com.facebook.React.BridgeMethodQueue", DISPATCH_QUEUE_SERIAL); _displayLink = [[RCTDisplayLink alloc] initWithBridge:self]; _frameUpdateObservers = [[NSMutableSet alloc] init]; _scheduledCalls = [[NSMutableArray alloc] init]; @@ -850,11 +852,14 @@ static id _latestJSExecutor; } } - // Get method queue + // Get method queues _queuesByID = [[RCTSparseArray alloc] init]; [_modulesByID enumerateObjectsUsingBlock:^(id module, NSNumber *moduleID, BOOL *stop) { if ([module respondsToSelector:@selector(methodQueue)]) { - _queuesByID[moduleID] = [module methodQueue] ?: dispatch_get_main_queue(); + dispatch_queue_t queue = [module methodQueue]; + if (queue) { + _queuesByID[moduleID] = queue; + } } }]; @@ -895,11 +900,6 @@ static id _latestJSExecutor; } else { [[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidLoadNotification object:self]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(reload) - name:RCTReloadNotification - object:nil]; } [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reload) @@ -1205,7 +1205,7 @@ static id _latestJSExecutor; [_modulesByID enumerateObjectsUsingBlock:^(id module, NSNumber *moduleID, BOOL *stop) { if ([module respondsToSelector:@selector(batchDidComplete)]) { dispatch_queue_t queue = _queuesByID[moduleID]; - dispatch_async(queue ?: dispatch_get_main_queue(), ^{ + dispatch_async(queue ?: _methodQueue, ^{ [module batchDidComplete]; }); } @@ -1240,7 +1240,7 @@ static id _latestJSExecutor; __weak RCTBridge *weakSelf = self; dispatch_queue_t queue = _queuesByID[moduleID]; - dispatch_async(queue ?: dispatch_get_main_queue(), ^{ + dispatch_async(queue ?: _methodQueue, ^{ RCTProfileBeginEvent(); __strong RCTBridge *strongSelf = weakSelf; @@ -1320,13 +1320,15 @@ static id _latestJSExecutor; - (void)reload { - if (!_loading) { - // If the bridge has not loaded yet, the context will be already invalid at - // the time the javascript gets executed. - // It will crash the javascript, and even the next `load` won't render. - [self invalidate]; - [self setUp]; - } + dispatch_async(dispatch_get_main_queue(), ^{ + if (!_loading) { + // If the bridge has not loaded yet, the context will be already invalid at + // the time the javascript gets executed. + // It will crash the javascript, and even the next `load` won't render. + [self invalidate]; + [self setUp]; + } + }); } + (void)logMessage:(NSString *)message level:(NSString *)level diff --git a/React/Base/RCTBridgeModule.h b/React/Base/RCTBridgeModule.h index c8fa41c44..12f7803ea 100644 --- a/React/Base/RCTBridgeModule.h +++ b/React/Base/RCTBridgeModule.h @@ -91,18 +91,32 @@ typedef void (^RCTResponseSenderBlock)(NSArray *response); /** * The queue that will be used to call all exported methods. If omitted, this - * will default the main queue, which is recommended for any methods that - * interact with UIKit. If your methods perform heavy work such as filesystem - * or network access, you should return a custom serial queue. Example: + * will call on the default background queue, which is avoids blocking the main + * thread. + * + * If the methods in your module need to interact with UIKit methods, they will + * probably need to call those on the main thread, as most of UIKit is main- + * thread-only. You can tell React Native to call your module methods on the + * main thread by returning a reference to the main queue, like this: + * + * - (dispatch_queue_t)methodQueue + * { + * return dispatch_get_main_queue(); + * } + * + * If your methods perform heavy work such as synchronous filesystem or network + * access, you probably don't want to block the default background queue, as + * this will stall other methods. Instead, you should return a custom serial + * queue, like this: * * - (dispatch_queue_t)methodQueue * { * return dispatch_queue_create("com.mydomain.FileQueue", DISPATCH_QUEUE_SERIAL); * } * - * Alternatively, if only some methods on the module should be executed on a - * background queue you can leave this method unimplemented, and simply - * dispatch_async() within the method itself. + * Alternatively, if only some methods of the module should be executed on a + * particular queue you can leave this method unimplemented, and simply + * dispatch_async() to the required queue within the method itself. */ - (dispatch_queue_t)methodQueue; diff --git a/React/Modules/RCTAlertManager.m b/React/Modules/RCTAlertManager.m index b97364e38..2690de1df 100644 --- a/React/Modules/RCTAlertManager.m +++ b/React/Modules/RCTAlertManager.m @@ -35,6 +35,11 @@ RCT_EXPORT_MODULE() return self; } +- (dispatch_queue_t)methodQueue +{ + return dispatch_get_main_queue(); +} + /** * @param {NSDictionary} args Dictionary of the form * diff --git a/React/Modules/RCTStatusBarManager.m b/React/Modules/RCTStatusBarManager.m index 149ad568e..04bb39038 100644 --- a/React/Modules/RCTStatusBarManager.m +++ b/React/Modules/RCTStatusBarManager.m @@ -26,6 +26,11 @@ static BOOL RCTViewControllerBasedStatusBarAppearance() RCT_EXPORT_MODULE() +- (dispatch_queue_t)methodQueue +{ + return dispatch_get_main_queue(); +} + RCT_EXPORT_METHOD(setStyle:(UIStatusBarStyle)statusBarStyle animated:(BOOL)animated) { diff --git a/React/Modules/RCTTiming.m b/React/Modules/RCTTiming.m index 1f6e84d6a..62d42a7bb 100644 --- a/React/Modules/RCTTiming.m +++ b/React/Modules/RCTTiming.m @@ -108,6 +108,11 @@ RCT_IMPORT_METHOD(RCTJSTimers, callTimers) [[NSNotificationCenter defaultCenter] removeObserver:self]; } +- (dispatch_queue_t)methodQueue +{ + return dispatch_get_main_queue(); +} + - (BOOL)isValid { return _bridge != nil;