diff --git a/React/CxxBridge/RCTCxxBridge.mm b/React/CxxBridge/RCTCxxBridge.mm index 937c0997acefd6..202ff06f037096 100644 --- a/React/CxxBridge/RCTCxxBridge.mm +++ b/React/CxxBridge/RCTCxxBridge.mm @@ -61,6 +61,8 @@ @interface RCTCxxBridge : RCTBridge static NSString *const RCTJSThreadName = @"com.facebook.react.JavaScript"; +typedef void (^RCTPendingCall)(); + using namespace facebook::react; /** @@ -157,7 +159,7 @@ @implementation RCTCxxBridge BOOL _wasBatchActive; BOOL _didInvalidate; - NSMutableArray *_pendingCalls; + NSMutableArray *_pendingCalls; std::atomic _pendingCount; // Native modules @@ -972,7 +974,7 @@ - (void)logMessage:(NSString *)message level:(NSString *)level #pragma mark - RCTBridge methods -- (void)_runAfterLoad:(dispatch_block_t)block +- (void)_runAfterLoad:(RCTPendingCall)block { // Ordering here is tricky. Ideally, the C++ bridge would provide // functionality to defer calls until after the app is loaded. Until that @@ -1025,9 +1027,9 @@ - (void)_flushPendingCalls // Phase B: _flushPendingCalls happens. Each block in _pendingCalls is // executed, adding work to the queue, and _pendingCount is decremented. // loading is set to NO. - NSArray *pendingCalls = _pendingCalls; + NSArray *pendingCalls = _pendingCalls; _pendingCalls = nil; - for (dispatch_block_t call in pendingCalls) { + for (RCTPendingCall call in pendingCalls) { call(); _pendingCount--; } @@ -1050,18 +1052,23 @@ - (void)enqueueJSCall:(NSString *)module method:(NSString *)method args:(NSArray RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"-[RCTCxxBridge enqueueJSCall:]", nil); RCTProfileBeginFlowEvent(); - [self _runAfterLoad:^{ + __weak __typeof(self) weakSelf = self; + [self _runAfterLoad:^(){ RCTProfileEndFlowEvent(); + __strong __typeof(weakSelf) strongSelf = weakSelf; + if (!strongSelf) { + return; + } - if (self->_reactInstance) { - self->_reactInstance->callJSFunction([module UTF8String], [method UTF8String], - convertIdToFollyDynamic(args ?: @[])); + if (strongSelf->_reactInstance) { + strongSelf->_reactInstance->callJSFunction([module UTF8String], [method UTF8String], + convertIdToFollyDynamic(args ?: @[])); // ensureOnJavaScriptThread may execute immediately, so use jsMessageThread, to make sure // the block is invoked after callJSFunction if (completion) { - if (self->_jsMessageThread) { - self->_jsMessageThread->runOnQueue(completion); + if (strongSelf->_jsMessageThread) { + strongSelf->_jsMessageThread->runOnQueue(completion); } else { RCTLogWarn(@"Can't invoke completion without messageThread"); } @@ -1086,11 +1093,16 @@ - (void)enqueueCallback:(NSNumber *)cbID args:(NSArray *)args */ RCTProfileBeginFlowEvent(); - [self _runAfterLoad:^{ + __weak __typeof(self) weakSelf = self; + [self _runAfterLoad:^(){ RCTProfileEndFlowEvent(); + __strong __typeof(weakSelf) strongSelf = weakSelf; + if (!strongSelf) { + return; + } - if (self->_reactInstance) { - self->_reactInstance->callJSCallback([cbID unsignedLongLongValue], convertIdToFollyDynamic(args ?: @[])); + if (strongSelf->_reactInstance) { + strongSelf->_reactInstance->callJSCallback([cbID unsignedLongLongValue], convertIdToFollyDynamic(args ?: @[])); } }]; }