Skip to content

Commit 8608281

Browse files
Abbondanzofacebook-github-bot
authored andcommitted
Process Settings notifications off the posting thread
Summary: `SettingsManager` cleans the entire user-defaults dictionary inline whenever a defaults change notification arrives. A write on the main thread therefore performs unrelated, potentially expensive work before returning. Process these notifications on a serial background method queue. Coalesce pending notifications to the latest defaults state, while allowing a notification during a snapshot to schedule a trailing update. Keep JavaScript mutations on the same queue and suppress their notifications at posting time. Drop queued work after invalidation without blocking teardown or retaining the module. The `settingsUpdated` event name and full JSON-cleaned payload remain unchanged. Rapid external changes may be reported as one latest-state update rather than separate intermediate transitions. Synchronous constants export is unchanged. Changelog: [iOS][Fixed] - Avoid blocking user-defaults writers while processing Settings change notifications Differential Revision: D124504338
1 parent 1c6e0c7 commit 8608281

2 files changed

Lines changed: 380 additions & 6 deletions

File tree

‎packages/react-native/Libraries/Settings/RCTSettingsManager.mm‎

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,22 @@
1111
#import <React/RCTBridge.h>
1212
#import <React/RCTConvert.h>
1313
#import <React/RCTEventDispatcherProtocol.h>
14+
#import <React/RCTInvalidating.h>
1415
#import <React/RCTUtils.h>
1516

17+
#include <atomic>
18+
1619
#import "RCTSettingsPlugins.h"
1720

18-
@interface RCTSettingsManager () <NativeSettingsManagerSpec>
21+
@interface RCTSettingsManager () <NativeSettingsManagerSpec, RCTInvalidating>
1922
@end
2023

2124
@implementation RCTSettingsManager {
22-
BOOL _ignoringUpdates;
25+
std::atomic<bool> _ignoringUpdates;
26+
std::atomic<bool> _updateScheduled;
27+
std::atomic<bool> _invalidated;
2328
NSUserDefaults *_defaults;
29+
dispatch_queue_t _methodQueue;
2430
}
2531

2632
@synthesize moduleRegistry = _moduleRegistry;
@@ -40,7 +46,11 @@ - (instancetype)init
4046
- (instancetype)initWithUserDefaults:(NSUserDefaults *)defaults
4147
{
4248
if ((self = [super init]) != nullptr) {
49+
_ignoringUpdates = false;
50+
_updateScheduled = false;
51+
_invalidated = false;
4352
_defaults = defaults;
53+
_methodQueue = dispatch_queue_create("com.facebook.react.SettingsManager", DISPATCH_QUEUE_SERIAL);
4454

4555
[[NSNotificationCenter defaultCenter] addObserver:self
4656
selector:@selector(userDefaultsDidChange:)
@@ -61,18 +71,47 @@ - (instancetype)initWithUserDefaults:(NSUserDefaults *)defaults
6171
{.settings = RCTJSONClean([_defaults dictionaryRepresentation])});
6272
}
6373

74+
- (dispatch_queue_t)methodQueue
75+
{
76+
return _methodQueue;
77+
}
78+
79+
- (void)invalidate
80+
{
81+
_invalidated = true;
82+
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSUserDefaultsDidChangeNotification object:_defaults];
83+
}
84+
6485
- (void)userDefaultsDidChange:(NSNotification *)note
6586
{
66-
if (_ignoringUpdates) {
87+
if (_ignoringUpdates || _invalidated || _updateScheduled.exchange(true)) {
6788
return;
6889
}
6990

91+
__weak RCTSettingsManager *weakSelf = self;
92+
dispatch_async(_methodQueue, ^{
93+
RCTSettingsManager *strongSelf = weakSelf;
94+
if (strongSelf == nil) {
95+
return;
96+
}
97+
98+
// Clear before reading so a notification during the snapshot schedules a trailing update.
99+
strongSelf->_updateScheduled = false;
100+
if (strongSelf->_invalidated) {
101+
return;
102+
}
103+
104+
NSDictionary *settings = RCTJSONClean([strongSelf->_defaults dictionaryRepresentation]);
105+
if (strongSelf->_invalidated) {
106+
return;
107+
}
108+
70109
#pragma clang diagnostic push
71110
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
72-
[[_moduleRegistry moduleForName:"EventDispatcher"]
73-
sendDeviceEventWithName:@"settingsUpdated"
74-
body:RCTJSONClean([_defaults dictionaryRepresentation])];
111+
[[strongSelf->_moduleRegistry moduleForName:"EventDispatcher"] sendDeviceEventWithName:@"settingsUpdated"
112+
body:settings];
75113
#pragma clang diagnostic pop
114+
});
76115
}
77116

78117
/**

0 commit comments

Comments
 (0)