mirror of
https://github.com/status-im/react-native.git
synced 2025-02-04 13:44:04 +00:00
Move setBridge: off main thread
Summary: Previously, if a module implemented `setBridge:` we assumed that it needs to be initialised on the main thread. This assumption was not really warranted however, and it was a barrier to deferring module initialization. This diff tweaks the rules so that only modules that override `init` or `constantsToExport**` are assumed to require main thread initialization, and others can be created lazily when they are first used. WARNING: this will be a breaking change to any 3rd party modules that are assuming `setBridge:` is called on the main thread. Those modules should be rewritten to move any code that requires the main thread into `init` or `constantsToExport` instead. `**` We will also be examining whether `constantsToExport` can be done lazily, but for now any module that uses it will still be created eagerly when the bridge starts up. Reviewed By: javache Differential Revision: D3240682 fb-gh-sync-id: 48f309e3158bbccb52141032baf70def3e609371 fbshipit-source-id: 48f309e3158bbccb52141032baf70def3e609371
This commit is contained in:
parent
55c8158c44
commit
34ec6a91a9
@ -221,10 +221,16 @@ RCT_EXPORT_MODULE()
|
||||
|
||||
- (void)testCustomSetBridgeModuleInitializedAtBridgeStartup
|
||||
{
|
||||
RUN_RUNLOOP_WHILE(!_customSetBridgeModuleNotificationSent);
|
||||
XCTAssertFalse(_customSetBridgeModuleNotificationSent);
|
||||
|
||||
__block RCTTestCustomSetBridgeModule *module;
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
module = [_bridge moduleForClass:[RCTTestCustomSetBridgeModule class]];
|
||||
});
|
||||
|
||||
RUN_RUNLOOP_WHILE(!module);
|
||||
XCTAssertTrue(_customSetBridgeModuleNotificationSent);
|
||||
RCTTestCustomSetBridgeModule *module = [_bridge moduleForClass:[RCTTestCustomSetBridgeModule class]];
|
||||
XCTAssertTrue(module.setBridgeOnMainThread);
|
||||
XCTAssertFalse(module.setBridgeOnMainThread);
|
||||
XCTAssertEqual(module.bridge, _bridge.batchedBridge);
|
||||
XCTAssertNotNil(module.methodQueue);
|
||||
}
|
||||
|
@ -37,19 +37,15 @@
|
||||
_instanceLock = [NSLock new];
|
||||
|
||||
static IMP objectInitMethod;
|
||||
static SEL setBridgeSelector;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
objectInitMethod = [NSObject instanceMethodForSelector:@selector(init)];
|
||||
setBridgeSelector = NSSelectorFromString(@"setBridge:");
|
||||
});
|
||||
|
||||
// If a module overrides `init`, `setBridge:` then we must assume that it
|
||||
// expects for both of those methods to be called on the main thread, because
|
||||
// they may need to access UIKit.
|
||||
_requiresMainThreadSetup =
|
||||
[_moduleClass instancesRespondToSelector:setBridgeSelector] ||
|
||||
(!_instance && [_moduleClass instanceMethodForSelector:@selector(init)] != objectInitMethod);
|
||||
// If a module overrides `init` then we must assume that it expects to be
|
||||
// initialized on the main thread, because it may need to access UIKit.
|
||||
_requiresMainThreadSetup = !_instance &&
|
||||
[_moduleClass instanceMethodForSelector:@selector(init)] != objectInitMethod;
|
||||
|
||||
// If a module overrides `constantsToExport` then we must assume that it
|
||||
// must be called on the main thread, because it may need to access UIKit.
|
||||
|
@ -258,6 +258,10 @@ CGFloat RCTScreenScale()
|
||||
|
||||
CGSize RCTScreenSize()
|
||||
{
|
||||
// FIXME: this caches the bounds at app start, whatever those were, and then
|
||||
// doesn't update when the device is rotated. We need to find another thread-
|
||||
// safe way to get the screen size.
|
||||
|
||||
static CGSize size;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
|
@ -43,13 +43,20 @@ RCT_EXPORT_MODULE()
|
||||
|
||||
#pragma mark - Lifecycle
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
|
||||
// Needs to be called on the main thread, as it accesses UIApplication
|
||||
_lastKnownState = RCTCurrentAppBackgroundState();
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setBridge:(RCTBridge *)bridge
|
||||
{
|
||||
_bridge = bridge;
|
||||
|
||||
// Is this thread-safe?
|
||||
_lastKnownState = RCTCurrentAppBackgroundState();
|
||||
|
||||
for (NSString *name in @[UIApplicationDidBecomeActiveNotification,
|
||||
UIApplicationDidEnterBackgroundNotification,
|
||||
UIApplicationDidFinishLaunchingNotification,
|
||||
|
@ -40,6 +40,13 @@ RCT_EXPORT_MODULE()
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
// We're only overriding this to ensure the module gets created at startup
|
||||
// TODO (D3175632): Remove once we have more declarative control over module setup.
|
||||
return [super init];
|
||||
}
|
||||
|
||||
- (void)setBridge:(RCTBridge *)bridge
|
||||
{
|
||||
_bridge = bridge;
|
||||
|
@ -730,7 +730,7 @@ RCT_EXPORT_METHOD(removeSubviewsFromContainerWithID:(nonnull NSNumber *)containe
|
||||
|
||||
- (void)_removeChildren:(NSArray<id<RCTComponent>> *)children
|
||||
fromContainer:(id<RCTComponent>)container
|
||||
permanent: (BOOL)permanent
|
||||
permanent:(BOOL)permanent
|
||||
{
|
||||
RCTLayoutAnimation *layoutAnimation = _layoutAnimation;
|
||||
RCTAnimation *deleteAnimation = layoutAnimation.deleteAnimation;
|
||||
|
@ -125,6 +125,25 @@ static vm_size_t RCTGetResidentMemorySize(void)
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
// We're only overriding this to ensure the module gets created at startup
|
||||
// TODO (D3175632): Remove once we have more declarative control over module setup.
|
||||
return [super init];
|
||||
}
|
||||
|
||||
- (dispatch_queue_t)methodQueue
|
||||
{
|
||||
return dispatch_get_main_queue();
|
||||
}
|
||||
|
||||
- (void)setBridge:(RCTBridge *)bridge
|
||||
{
|
||||
_bridge = bridge;
|
||||
|
||||
[_bridge.devMenu addItem:self.devMenuItem];
|
||||
}
|
||||
|
||||
- (void)invalidate
|
||||
{
|
||||
[self hide];
|
||||
@ -272,18 +291,6 @@ RCT_EXPORT_MODULE()
|
||||
return _metrics;
|
||||
}
|
||||
|
||||
- (dispatch_queue_t)methodQueue
|
||||
{
|
||||
return dispatch_get_main_queue();
|
||||
}
|
||||
|
||||
- (void)setBridge:(RCTBridge *)bridge
|
||||
{
|
||||
_bridge = bridge;
|
||||
|
||||
[_bridge.devMenu addItem:self.devMenuItem];
|
||||
}
|
||||
|
||||
- (void)show
|
||||
{
|
||||
if (_container) {
|
||||
|
@ -30,7 +30,7 @@ typedef void (^RCTViewManagerUIBlock)(RCTUIManager *uiManager, NSDictionary<NSNu
|
||||
* allowing the manager (or the views that it manages) to manipulate the view
|
||||
* hierarchy and send events back to the JS context.
|
||||
*/
|
||||
@property (nonatomic, weak, readonly) RCTBridge *bridge;
|
||||
@property (nonatomic, weak) RCTBridge *bridge;
|
||||
|
||||
/**
|
||||
* This method instantiates a native view to be managed by the module. Override
|
||||
|
Loading…
x
Reference in New Issue
Block a user