Skip to content

Commit 8662d9d

Browse files
fkgozalifacebook-github-bot
authored andcommitted
TM iOS: Added backward-compatible ObjC invalidation logic
Summary: Some ObjC NativeModules conform to `RCTInvalidating` protocol and implements `invalidate` method. This is typically used to clean things up during bridge teardown or reload. In TurboModule system the invalidate method can be replaced by pure destructors, but for backward compatibility, we call existing invalidate method on each module during teardown. This also cleans up all existing LongLivedObject's. Reviewed By: mdvacca, RSNara Differential Revision: D15365655 fbshipit-source-id: 802844b39b5b7adb54970ea541f4d744bbf9e480
1 parent cd2f8c5 commit 8662d9d

File tree

6 files changed

+82
-26
lines changed

6 files changed

+82
-26
lines changed

React/Base/RCTBridge.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,12 @@ RCT_EXTERN NSString *const RCTBridgeWillDownloadScriptNotification;
8585
*/
8686
RCT_EXTERN NSString *const RCTBridgeDidDownloadScriptNotification;
8787

88+
/**
89+
* This notification fires right after the bridge finishes invalidating NativeModule
90+
* instances during teardown. Handle this notification to perform additional invalidation.
91+
*/
92+
RCT_EXTERN NSString *const RCTBridgeDidInvalidateModulesNotification;
93+
8894
/**
8995
* Key for the RCTSource object in the RCTBridgeDidDownloadScriptNotification
9096
* userInfo dictionary.

React/Base/RCTBridge.m

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
NSString *const RCTBridgeWillReloadNotification = @"RCTBridgeWillReloadNotification";
3434
NSString *const RCTBridgeWillDownloadScriptNotification = @"RCTBridgeWillDownloadScriptNotification";
3535
NSString *const RCTBridgeDidDownloadScriptNotification = @"RCTBridgeDidDownloadScriptNotification";
36+
NSString *const RCTBridgeDidInvalidateModulesNotification = @"RCTBridgeDidInvalidateModulesNotification";
3637
NSString *const RCTBridgeDidDownloadScriptNotificationSourceKey = @"source";
3738
NSString *const RCTBridgeDidDownloadScriptNotificationBridgeDescriptionKey = @"bridgeDescription";
3839

React/CxxBridge/RCTCxxBridge.mm

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1100,6 +1100,10 @@ - (void)invalidate
11001100
[moduleData invalidate];
11011101
}
11021102

1103+
[[NSNotificationCenter defaultCenter] postNotificationName:RCTBridgeDidInvalidateModulesNotification
1104+
object:self->_parentBridge
1105+
userInfo:@{@"bridge": self}];
1106+
11031107
if (dispatch_group_wait(moduleInvalidation, dispatch_time(DISPATCH_TIME_NOW, 10 * NSEC_PER_SEC))) {
11041108
RCTLogError(@"Timed out waiting for modules to be invalidated");
11051109
}

ReactCommon/turbomodule/core/TurboModuleBinding.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <string>
1111

1212
#include <cxxreact/SystraceSection.h>
13+
#include <jsireact/LongLivedObject.h>
1314

1415
using namespace facebook;
1516

@@ -38,7 +39,7 @@ void TurboModuleBinding::install(
3839
}
3940

4041
void TurboModuleBinding::invalidate() const {
41-
// Nothing for now.
42+
LongLivedObjectCollection::get().clear();
4243
}
4344

4445
std::shared_ptr<TurboModule> TurboModuleBinding::getModule(const std::string &name) {

ReactCommon/turbomodule/core/platform/ios/RCTTurboModuleManager.mm

Lines changed: 62 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@
1010
#import <cassert>
1111

1212
#import <React/RCTBridge+Private.h>
13-
#import <React/RCTPerformanceLogger.h>
1413
#import <React/RCTBridgeModule.h>
1514
#import <React/RCTCxxModule.h>
1615
#import <React/RCTLog.h>
16+
#import <React/RCTPerformanceLogger.h>
1717
#import <jsireact/BridgeJSCallInvoker.h>
1818
#import <jsireact/TurboCxxModule.h>
1919
#import <jsireact/TurboModuleBinding.h>
@@ -22,16 +22,16 @@
2222

2323
// Fallback lookup since RCT class prefix is sometimes stripped in the existing NativeModule system.
2424
// This will be removed in the future.
25-
static Class getFallbackClassFromName(const char *name) {
25+
static Class getFallbackClassFromName(const char *name)
26+
{
2627
Class moduleClass = NSClassFromString([NSString stringWithUTF8String:name]);
2728
if (!moduleClass) {
2829
moduleClass = NSClassFromString([NSString stringWithFormat:@"RCT%s", name]);
2930
}
3031
return moduleClass;
3132
}
3233

33-
@implementation RCTTurboModuleManager
34-
{
34+
@implementation RCTTurboModuleManager {
3535
jsi::Runtime *_runtime;
3636
std::shared_ptr<facebook::react::JSCallInvoker> _jsInvoker;
3737
std::shared_ptr<react::TurboModuleBinding> _binding;
@@ -57,6 +57,11 @@ - (instancetype)initWithBridge:(RCTBridge *)bridge delegate:(id<RCTTurboModuleMa
5757
// Necessary to allow NativeModules to lookup TurboModules
5858
[bridge setRCTTurboModuleLookupDelegate:self];
5959

60+
[[NSNotificationCenter defaultCenter] addObserver:self
61+
selector:@selector(bridgeDidInvalidateModules:)
62+
name:RCTBridgeDidInvalidateModulesNotification
63+
object:_bridge.parentBridge];
64+
6065
__weak __typeof(self) weakSelf = self;
6166

6267
auto moduleProvider = [weakSelf](const std::string &name) -> std::shared_ptr<react::TurboModule> {
@@ -92,17 +97,23 @@ - (instancetype)initWithBridge:(RCTBridge *)bridge delegate:(id<RCTTurboModuleMa
9297
return self;
9398
}
9499

95-
- (void)notifyAboutTurboModuleSetup:(const char*)name {
96-
NSString *moduleName = [[NSString alloc] initWithUTF8String:name];
97-
if (moduleName) {
98-
int64_t setupTime = [self->_bridge.performanceLogger durationForTag:RCTPLTurboModuleSetup];
99-
[[NSNotificationCenter defaultCenter] postNotificationName:RCTDidSetupModuleNotification
100-
object:nil
101-
userInfo:@{
102-
RCTDidSetupModuleNotificationModuleNameKey: moduleName,
103-
RCTDidSetupModuleNotificationSetupTimeKey: @(setupTime)
104-
}];
105-
}
100+
- (void)dealloc
101+
{
102+
[[NSNotificationCenter defaultCenter] removeObserver:self];
103+
}
104+
105+
- (void)notifyAboutTurboModuleSetup:(const char *)name
106+
{
107+
NSString *moduleName = [[NSString alloc] initWithUTF8String:name];
108+
if (moduleName) {
109+
int64_t setupTime = [self->_bridge.performanceLogger durationForTag:RCTPLTurboModuleSetup];
110+
[[NSNotificationCenter defaultCenter] postNotificationName:RCTDidSetupModuleNotification
111+
object:nil
112+
userInfo:@{
113+
RCTDidSetupModuleNotificationModuleNameKey : moduleName,
114+
RCTDidSetupModuleNotificationSetupTimeKey : @(setupTime)
115+
}];
116+
}
106117
}
107118

108119
/**
@@ -114,7 +125,7 @@ - (void)notifyAboutTurboModuleSetup:(const char*)name {
114125
* (for now).
115126
*/
116127

117-
- (std::shared_ptr<react::TurboModule>)provideTurboModule:(const char*)moduleName
128+
- (std::shared_ptr<react::TurboModule>)provideTurboModule:(const char *)moduleName
118129
{
119130
auto turboModuleLookup = _turboModuleCache.find(moduleName);
120131
if (turboModuleLookup != _turboModuleCache.end()) {
@@ -185,7 +196,7 @@ - (void)notifyAboutTurboModuleSetup:(const char*)name {
185196
* Note: All TurboModule instances are cached, which means they're all long-lived
186197
* (for now).
187198
*/
188-
- (id<RCTTurboModule>)provideRCTTurboModule:(const char*)moduleName
199+
- (id<RCTTurboModule>)provideRCTTurboModule:(const char *)moduleName
189200
{
190201
auto rctTurboModuleCacheLookup = _rctTurboModuleCache.find(moduleName);
191202
if (rctTurboModuleCacheLookup != _rctTurboModuleCache.end()) {
@@ -245,11 +256,12 @@ - (void)notifyAboutTurboModuleSetup:(const char*)name {
245256
* the bridge property of these NativeModules.
246257
*/
247258
[(id)module setValue:_bridge forKey:@"bridge"];
248-
}
249-
@catch (NSException *exception) {
250-
RCTLogError(@"%@ has no setter or ivar for its bridge, which is not "
251-
"permitted. You must either @synthesize the bridge property, "
252-
"or provide your own setter method.", RCTBridgeModuleNameForClass(module));
259+
} @catch (NSException *exception) {
260+
RCTLogError(
261+
@"%@ has no setter or ivar for its bridge, which is not "
262+
"permitted. You must either @synthesize the bridge property, "
263+
"or provide your own setter method.",
264+
RCTBridgeModuleNameForClass(module));
253265
}
254266
}
255267

@@ -265,9 +277,10 @@ - (void)notifyAboutTurboModuleSetup:(const char*)name {
265277
* TODO(T41180176): Investigate whether we can get rid of this after all
266278
* TurboModules are rolled out
267279
*/
268-
[[NSNotificationCenter defaultCenter] postNotificationName:RCTDidInitializeModuleNotification
269-
object:_bridge
270-
userInfo:@{@"module": module, @"bridge": RCTNullIfNil(_bridge.parentBridge)}];
280+
[[NSNotificationCenter defaultCenter]
281+
postNotificationName:RCTDidInitializeModuleNotification
282+
object:_bridge
283+
userInfo:@{@"module" : module, @"bridge" : RCTNullIfNil(_bridge.parentBridge)}];
271284
return module;
272285
}
273286

@@ -311,4 +324,28 @@ - (BOOL)moduleIsInitialized:(const char *)moduleName
311324
return _rctTurboModuleCache.find(std::string(moduleName)) != _rctTurboModuleCache.end();
312325
}
313326

327+
#pragma mark Invalidation logic
328+
329+
- (void)bridgeDidInvalidateModules:(NSNotification *)notification
330+
{
331+
// Rely on this notification to invalidate all known TurboModule ObjC instances, synchronously.
332+
RCTBridge *bridge = notification.userInfo[@"bridge"];
333+
if (bridge != _bridge) {
334+
return;
335+
}
336+
337+
// Backward-compatibility: RCTInvalidating handling.
338+
for (const auto &p : _rctTurboModuleCache) {
339+
id<RCTTurboModule> module = p.second;
340+
if ([module respondsToSelector:@selector(invalidate)]) {
341+
[((id<RCTInvalidating>)module) invalidate];
342+
}
343+
}
344+
345+
_rctTurboModuleCache.clear();
346+
_turboModuleCache.clear();
347+
348+
_binding->invalidate();
349+
}
350+
314351
@end

ReactCommon/turbomodule/samples/platform/ios/RCTSampleTurboModule.mm

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,13 @@ - (dispatch_queue_t)methodQueue
4444

4545
#endif
4646

47+
// Backward compatible invalidation
48+
- (void)invalidate
49+
{
50+
// Actually do nothing here.
51+
NSLog(@"Invalidating RCTSampleTurboModule...");
52+
}
53+
4754
- (NSDictionary *)getConstants
4855
{
4956
UIScreen *mainScreen = UIScreen.mainScreen;

0 commit comments

Comments
 (0)