From f7edcda5d7670ed71c7c7ddc989cbfc539080a52 Mon Sep 17 00:00:00 2001 From: Nick Lockwood Date: Tue, 15 Dec 2015 05:42:45 -0800 Subject: [PATCH] Deprecated RCTDidCreateNativeModules notification Summary: public Thanks to the new lazy initialization system for modules, `RCTDidCreateNativeModules` no longer does what the name implies. Previously, `RCTDidCreateNativeModules` was fired after all native modules had been initialized. Now, it simply fires each time the bridge is reloaded. Modules are created on demand when they are needed, so most of the assumptions about when `RCTDidCreateNativeModules` will fire are now incorrect. This diff deprecates `RCTDidCreateNativeModules`, and adds a new notification, `RCTDidInitializeModuleNotification`, which fires each time a module a new module is instantiated. If you need to access a module at any time you can just call `-[bridge moduleForClass:]` and the module will be instantiated on demand. If you want to access a module *only* after it has already been instantiated, you can use the `RCTDidInitializeModuleNotification` notification. Reviewed By: tadeuzagallo Differential Revision: D2755036 fb-gh-sync-id: 25bab6d5eb6fcd35d43125ac45908035eea01487 --- React/Base/RCTBatchedBridge.m | 48 +++++++++++++++++++++-------------- React/Base/RCTBridge.h | 34 ++++++++++++++++++++----- React/Base/RCTBridge.m | 12 +++++---- React/Base/RCTModuleData.m | 7 +++++ React/Modules/RCTUIManager.m | 4 +-- React/Profiler/RCTProfile.m | 4 +-- 6 files changed, 75 insertions(+), 34 deletions(-) diff --git a/React/Base/RCTBatchedBridge.m b/React/Base/RCTBatchedBridge.m index 21d35a6e1..9aba7f9ff 100644 --- a/React/Base/RCTBatchedBridge.m +++ b/React/Base/RCTBatchedBridge.m @@ -29,6 +29,9 @@ [[[NSThread currentThread] name] isEqualToString:@"com.facebook.React.JavaScript"], \ @"This method must be called on JS thread") +/** + * Used by RKAnalyticsCPULogger + */ NSString *const RCTEnqueueNotification = @"RCTEnqueueNotification"; NSString *const RCTDequeueNotification = @"RCTDequeueNotification"; @@ -90,9 +93,9 @@ RCT_EXTERN NSArray *RCTGetModuleClasses(void); [RCTBridge setCurrentBridge:self]; - [[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptWillStartLoadingNotification - object:self - userInfo:@{ @"bridge": self }]; + [[NSNotificationCenter defaultCenter] + postNotificationName:RCTJavaScriptWillStartLoadingNotification + object:_parentBridge userInfo:@{@"bridge": self}]; [self start]; } @@ -208,9 +211,9 @@ RCT_EXTERN NSArray *RCTGetModuleClasses(void); // Allow testing without a script dispatch_async(dispatch_get_main_queue(), ^{ [self didFinishLoading]; - [[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidLoadNotification - object:_parentBridge - userInfo:@{ @"bridge": self }]; + [[NSNotificationCenter defaultCenter] + postNotificationName:RCTJavaScriptDidLoadNotification + object:_parentBridge userInfo:@{@"bridge": self}]; }); onSourceLoad(nil, nil); } @@ -331,8 +334,15 @@ RCT_EXTERN NSArray *RCTGetModuleClasses(void); } } - [[NSNotificationCenter defaultCenter] postNotificationName:RCTDidCreateNativeModules - object:self]; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + + [[NSNotificationCenter defaultCenter] + postNotificationName:RCTDidCreateNativeModules + object:self userInfo:@{@"bridge": self}]; + +#pragma clang diagnostic pop + RCTPerformanceLoggerEnd(RCTPLNativeModuleInit); } @@ -429,10 +439,9 @@ RCT_EXTERN NSArray *RCTGetModuleClasses(void); // timing issues with RCTRootView dispatch_async(dispatch_get_main_queue(), ^{ [self didFinishLoading]; - - [[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidLoadNotification - object:_parentBridge - userInfo:@{ @"bridge": self }]; + [[NSNotificationCenter defaultCenter] + postNotificationName:RCTJavaScriptDidLoadNotification + object:_parentBridge userInfo:@{@"bridge": self}]; }); }]; } @@ -457,9 +466,10 @@ RCT_EXTERN NSArray *RCTGetModuleClasses(void); _loading = NO; - [[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidFailToLoadNotification - object:_parentBridge - userInfo:@{@"bridge": self, @"error": error}]; + [[NSNotificationCenter defaultCenter] + postNotificationName:RCTJavaScriptDidFailToLoadNotification + object:_parentBridge userInfo:@{@"bridge": self, @"error": error}]; + RCTFatal(error); } @@ -715,7 +725,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleUR { RCTAssertJSThread(); - [[NSNotificationCenter defaultCenter] postNotificationName:RCTEnqueueNotification object:nil userInfo:nil]; + [[NSNotificationCenter defaultCenter] postNotificationName:RCTEnqueueNotification object:self userInfo:nil]; RCTJavaScriptCallback processResponse = ^(id json, NSError *error) { if (error) { @@ -725,7 +735,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleUR if (!self.isValid) { return; } - [[NSNotificationCenter defaultCenter] postNotificationName:RCTDequeueNotification object:nil userInfo:nil]; + [[NSNotificationCenter defaultCenter] postNotificationName:RCTDequeueNotification object:self userInfo:nil]; [self handleBuffer:json batchEnded:YES]; }; @@ -740,7 +750,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleUR { RCTAssertJSThread(); - [[NSNotificationCenter defaultCenter] postNotificationName:RCTEnqueueNotification object:nil userInfo:nil]; + [[NSNotificationCenter defaultCenter] postNotificationName:RCTEnqueueNotification object:self userInfo:nil]; RCTJavaScriptCallback processResponse = ^(id json, NSError *error) { if (error) { @@ -750,7 +760,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleUR if (!self.isValid) { return; } - [[NSNotificationCenter defaultCenter] postNotificationName:RCTDequeueNotification object:nil userInfo:nil]; + [[NSNotificationCenter defaultCenter] postNotificationName:RCTDequeueNotification object:self userInfo:nil]; [self handleBuffer:json batchEnded:YES]; }; diff --git a/React/Base/RCTBridge.h b/React/Base/RCTBridge.h index f341b75aa..a67176e9c 100644 --- a/React/Base/RCTBridge.h +++ b/React/Base/RCTBridge.h @@ -25,24 +25,28 @@ RCT_EXTERN NSString *const RCTReloadNotification; /** - * This notification fires when the bridge starts loading. + * This notification fires when the bridge starts loading the JS bundle. */ RCT_EXTERN NSString *const RCTJavaScriptWillStartLoadingNotification; /** - * This notification fires when the bridge has finished loading. + * This notification fires when the bridge has finished loading the JS bundle. */ RCT_EXTERN NSString *const RCTJavaScriptDidLoadNotification; /** - * This notification fires when the bridge failed to load. + * This notification fires when the bridge failed to load the JS bundle. The + * `error` key can be used to determine the error that occured. */ RCT_EXTERN NSString *const RCTJavaScriptDidFailToLoadNotification; /** - * This notification fires when the bridge created all registered native modules + * This notification fires each time a native module is instantiated. The + * `module` key will contain a reference to the newly-created module instance. + * Note that this notification may be fired before the module is available via + * the `[bridge moduleForClass:]` method. */ -RCT_EXTERN NSString *const RCTDidCreateNativeModules; +RCT_EXTERN NSString *const RCTDidInitializeModuleNotification; /** * This block can be used to instantiate modules that require additional @@ -171,10 +175,28 @@ RCT_EXTERN BOOL RCTBridgeModuleClassIsRegistered(Class); @end /** - * These properties and methods are deprecated and should not be used + * These features are deprecated and should not be used. */ @interface RCTBridge (Deprecated) +/** + * This notification used to fire after all native modules has been initialized, + * but now that native modules are instantiated lazily on demand, its original + * purpose is meaningless. + * + * If you need to access a module, you can do so as soon as the bridge has been + * initialized, by calling `[bridge moduleForClass:]`. If you need to know when + * an individual module has been instantiated, use the `RCTDidInitializeModule` + * notification instead. + */ +RCT_EXTERN NSString *const RCTDidCreateNativeModules +__deprecated_msg("Use RCTDidInitializeModule to observe init of individual modules"); + +/** + * Accessing the modules property causes all modules to be eagerly initialized, + * which stalls the main thread. Use moduleClasses to enumerate through modules + * without causing them to be instantiated. + */ @property (nonatomic, copy, readonly) NSDictionary *modules __deprecated_msg("Use moduleClasses and/or moduleForName: instead"); diff --git a/React/Base/RCTBridge.m b/React/Base/RCTBridge.m index 1dfac45b3..6ba6b5c36 100644 --- a/React/Base/RCTBridge.m +++ b/React/Base/RCTBridge.m @@ -22,7 +22,7 @@ NSString *const RCTReloadNotification = @"RCTReloadNotification"; NSString *const RCTJavaScriptWillStartLoadingNotification = @"RCTJavaScriptWillStartLoadingNotification"; NSString *const RCTJavaScriptDidLoadNotification = @"RCTJavaScriptDidLoadNotification"; NSString *const RCTJavaScriptDidFailToLoadNotification = @"RCTJavaScriptDidFailToLoadNotification"; -NSString *const RCTDidCreateNativeModules = @"RCTDidCreateNativeModules"; +NSString *const RCTDidInitializeModuleNotification = @"RCTDidInitializeModuleNotification"; @interface RCTBatchedBridge : RCTBridge @@ -213,10 +213,10 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init) [commands registerKeyCommandWithInput:@"r" modifierFlags:UIKeyModifierCommand action:^(__unused UIKeyCommand *command) { - [[NSNotificationCenter defaultCenter] postNotificationName:RCTReloadNotification - object:nil - userInfo:nil]; - }]; + [[NSNotificationCenter defaultCenter] postNotificationName:RCTReloadNotification + object:nil + userInfo:nil]; + }]; #endif } @@ -305,6 +305,8 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init) @implementation RCTBridge(Deprecated) +NSString *const RCTDidCreateNativeModules = @"RCTDidCreateNativeModules"; + - (NSDictionary *)modules { return self.batchedBridge.modules; diff --git a/React/Base/RCTModuleData.m b/React/Base/RCTModuleData.m index d247f532f..9b28cd657 100644 --- a/React/Base/RCTModuleData.m +++ b/React/Base/RCTModuleData.m @@ -79,6 +79,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init); - (void)setBridgeForInstance:(RCTBridge *)bridge { + _bridge = bridge; if ([_instance respondsToSelector:@selector(bridge)]) { @try { [(id)_instance setValue:bridge forKey:@"bridge"]; @@ -198,6 +199,12 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init); } } } + + // Needs to be sent after bridge has been set for all module instances. + // Makes sense to put it here, since the same rules apply for methodQueue. + [[NSNotificationCenter defaultCenter] + postNotificationName:RCTDidInitializeModuleNotification + object:_bridge userInfo:@{@"module": _instance}]; } return _methodQueue; } diff --git a/React/Modules/RCTUIManager.m b/React/Modules/RCTUIManager.m index 46c991e3c..bf1c2ee59 100644 --- a/React/Modules/RCTUIManager.m +++ b/React/Modules/RCTUIManager.m @@ -347,7 +347,7 @@ extern NSString *RCTBridgeModuleNameForClass(Class cls); [[NSNotificationCenter defaultCenter] postNotificationName:RCTUIManagerDidRegisterRootViewNotification object:self - userInfo:@{ RCTUIManagerRootViewKey: rootView }]; + userInfo:@{RCTUIManagerRootViewKey: rootView}]; } - (UIView *)viewForReactTag:(NSNumber *)reactTag @@ -695,7 +695,7 @@ RCT_EXPORT_METHOD(removeRootView:(nonnull NSNumber *)rootReactTag) [[NSNotificationCenter defaultCenter] postNotificationName:RCTUIManagerDidRemoveRootViewNotification object:uiManager - userInfo:@{ RCTUIManagerRootViewKey: rootView }]; + userInfo:@{RCTUIManagerRootViewKey: rootView}]; }]; } diff --git a/React/Profiler/RCTProfile.m b/React/Profiler/RCTProfile.m index 9cc0ae1d9..122dcbe8f 100644 --- a/React/Profiler/RCTProfile.m +++ b/React/Profiler/RCTProfile.m @@ -403,7 +403,7 @@ void RCTProfileInit(RCTBridge *bridge) forMode:NSRunLoopCommonModes]; [[NSNotificationCenter defaultCenter] postNotificationName:RCTProfileDidStartProfiling - object:nil]; + object:bridge]; } void RCTProfileEnd(RCTBridge *bridge, void (^callback)(NSString *)) @@ -417,7 +417,7 @@ void RCTProfileEnd(RCTBridge *bridge, void (^callback)(NSString *)) OSAtomicAnd32Barrier(0, &RCTProfileProfiling); [[NSNotificationCenter defaultCenter] postNotificationName:RCTProfileDidEndProfiling - object:nil]; + object:bridge]; [RCTProfileDisplayLink invalidate]; RCTProfileDisplayLink = nil;