diff --git a/React/Base/RCTBridge.m b/React/Base/RCTBridge.m index fd3ecaa24..f442f1cf4 100644 --- a/React/Base/RCTBridge.m +++ b/React/Base/RCTBridge.m @@ -1144,18 +1144,11 @@ RCT_BRIDGE_WARN(_invokeAndProcessModule:(NSString *)module method:(NSString *)me _latestJSExecutor = nil; } - /** - * Main Thread deallocations - */ - [[NSNotificationCenter defaultCenter] removeObserver:self]; - [_mainDisplayLink invalidate]; + void (^mainThreadInvalidate)(void) = ^{ - [_javaScriptExecutor executeBlockOnJavaScriptQueue:^{ - /** - * JS Thread deallocations - */ - [_javaScriptExecutor invalidate]; - [_jsDisplayLink invalidate]; + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [_mainDisplayLink invalidate]; + _mainDisplayLink = nil; // Invalidate modules for (id target in _modulesByID.allObjects) { @@ -1165,11 +1158,35 @@ RCT_BRIDGE_WARN(_invokeAndProcessModule:(NSString *)module method:(NSString *)me } // Release modules (breaks retain cycle if module has strong bridge reference) - _javaScriptExecutor = nil; _frameUpdateObservers = nil; _modulesByID = nil; _queuesByID = nil; _modulesByName = nil; + }; + + if (!_javaScriptExecutor) { + + // No JS thread running + mainThreadInvalidate(); + return; + } + + [_javaScriptExecutor executeBlockOnJavaScriptQueue:^{ + + /** + * JS Thread deallocations + */ + [_javaScriptExecutor invalidate]; + _javaScriptExecutor = nil; + + [_jsDisplayLink invalidate]; + _jsDisplayLink = nil; + + /** + * Main Thread deallocations + */ + mainThreadInvalidate(); + }]; } diff --git a/React/Base/RCTDevMenu.m b/React/Base/RCTDevMenu.m index 3d88caf55..f7e688df5 100644 --- a/React/Base/RCTDevMenu.m +++ b/React/Base/RCTDevMenu.m @@ -73,9 +73,6 @@ RCT_EXPORT_MODULE() { if ((self = [super init])) { - _defaults = [NSUserDefaults standardUserDefaults]; - [self updateSettings]; - NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; [notificationCenter addObserver:self @@ -93,19 +90,27 @@ RCT_EXPORT_MODULE() name:RCTJavaScriptDidLoadNotification object:nil]; + _defaults = [NSUserDefaults standardUserDefaults]; + _settings = [[NSMutableDictionary alloc] init]; + + // Delay setup until after Bridge init + __weak RCTDevMenu *weakSelf = self; + dispatch_async(dispatch_get_main_queue(), ^{ + [weakSelf updateSettings]; + }); + #if TARGET_IPHONE_SIMULATOR - __weak RCTDevMenu *weakSelf = self; RCTKeyCommands *commands = [RCTKeyCommands sharedInstance]; - // toggle debug menu + // Toggle debug menu [commands registerKeyCommandWithInput:@"d" modifierFlags:UIKeyModifierCommand action:^(UIKeyCommand *command) { [weakSelf toggle]; }]; - // reload in normal mode + // Reload in normal mode [commands registerKeyCommandWithInput:@"n" modifierFlags:UIKeyModifierCommand action:^(UIKeyCommand *command) { @@ -117,22 +122,23 @@ RCT_EXPORT_MODULE() return self; } +- (dispatch_queue_t)methodQueue +{ + return dispatch_get_main_queue(); +} + - (void)updateSettings { - __weak RCTDevMenu *weakSelf = self; - dispatch_async(dispatch_get_main_queue(), ^{ - RCTDevMenu *strongSelf = weakSelf; - if (!strongSelf) { - return; - } + NSDictionary *settings = [_defaults objectForKey:RCTDevMenuSettingsKey]; + if ([settings isEqualToDictionary:_settings]) { + return; + } - strongSelf->_settings = [NSMutableDictionary dictionaryWithDictionary:[strongSelf->_defaults objectForKey:RCTDevMenuSettingsKey]]; - - strongSelf.shakeToShow = [strongSelf->_settings[@"shakeToShow"] ?: @YES boolValue]; - strongSelf.profilingEnabled = [strongSelf->_settings[@"profilingEnabled"] ?: @NO boolValue]; - strongSelf.liveReloadEnabled = [strongSelf->_settings[@"liveReloadEnabled"] ?: @NO boolValue]; - strongSelf.executorClass = NSClassFromString(strongSelf->_settings[@"executorClass"]); - }); + [_settings setDictionary:settings]; + self.shakeToShow = [_settings[@"shakeToShow"] ?: @YES boolValue]; + self.profilingEnabled = [_settings[@"profilingEnabled"] ?: @NO boolValue]; + self.liveReloadEnabled = [_settings[@"liveReloadEnabled"] ?: @NO boolValue]; + self.executorClass = NSClassFromString(_settings[@"executorClass"]); } - (void)jsLoaded @@ -161,6 +167,11 @@ RCT_EXPORT_MODULE() }); } +- (BOOL)isValid +{ + return NO; +} + - (void)dealloc { [_updateTask cancel]; @@ -170,6 +181,10 @@ RCT_EXPORT_MODULE() - (void)updateSetting:(NSString *)name value:(id)value { + id currentValue = _settings[name]; + if (currentValue == value || [currentValue isEqual:value]) { + return; + } if (value) { _settings[name] = value; } else { @@ -239,6 +254,9 @@ RCT_EXPORT_METHOD(reload) - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex { _actionSheet = nil; + if (buttonIndex == actionSheet.cancelButtonIndex) { + return; + } switch (buttonIndex) { case 0: { diff --git a/React/Base/RCTRedBox.m b/React/Base/RCTRedBox.m index 0de61d172..9c5b6d3dd 100644 --- a/React/Base/RCTRedBox.m +++ b/React/Base/RCTRedBox.m @@ -76,10 +76,27 @@ reloadButton.frame = CGRectMake(buttonWidth, self.bounds.size.height - buttonHeight, buttonWidth, buttonHeight); [_rootView addSubview:dismissButton]; [_rootView addSubview:reloadButton]; + + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + + [notificationCenter addObserver:self + selector:@selector(dismiss) + name:RCTReloadNotification + object:nil]; + + [notificationCenter addObserver:self + selector:@selector(dismiss) + name:RCTJavaScriptDidLoadNotification + object:nil]; } return self; } +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + - (void)openStackFrameInEditor:(NSDictionary *)stackFrame { NSData *stackFrameJSON = [RCTJSONStringify(stackFrame, nil) dataUsingEncoding:NSUTF8StringEncoding]; @@ -125,7 +142,6 @@ - (void)reload { [[NSNotificationCenter defaultCenter] postNotificationName:RCTReloadNotification object:nil userInfo:nil]; - [self dismiss]; } #pragma mark - TableView