mirror of
https://github.com/status-im/react-native.git
synced 2025-02-04 21:53:30 +00:00
Added in-memory cache for AsyncLocalStorage
Reviewed By: tadeuzagallo Differential Revision: D2641705 fb-gh-sync-id: 40b96b3084b82779e16f8845f9faeb0e3638d189
This commit is contained in:
parent
90c8f37c39
commit
397791fcea
@ -122,6 +122,23 @@ static dispatch_queue_t RCTGetMethodQueue()
|
||||
return queue;
|
||||
}
|
||||
|
||||
static NSCache *RCTGetCache()
|
||||
{
|
||||
// We want all instances to share the same cache since they will be reading/writing the same files.
|
||||
static NSCache *cache;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
cache = [NSCache new];
|
||||
cache.totalCostLimit = 2 * 1024 * 1024; // 2MB
|
||||
|
||||
// Clear cache in the event of a memory warning
|
||||
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidReceiveMemoryWarningNotification object:nil queue:nil usingBlock:^(__unused NSNotification *note) {
|
||||
[cache removeAllObjects];
|
||||
}];
|
||||
});
|
||||
return cache;
|
||||
}
|
||||
|
||||
static BOOL RCTHasCreatedStorageDirectory = NO;
|
||||
static NSDictionary *RCTDeleteStorageDirectory()
|
||||
{
|
||||
@ -139,7 +156,7 @@ static NSDictionary *RCTDeleteStorageDirectory()
|
||||
// The manifest is a dictionary of all keys with small values inlined. Null values indicate values that are stored
|
||||
// in separate files (as opposed to nil values which don't exist). The manifest is read off disk at startup, and
|
||||
// written to disk after all mutations.
|
||||
NSMutableDictionary *_manifest;
|
||||
NSMutableDictionary<NSString *, NSString *> *_manifest;
|
||||
}
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
@ -152,7 +169,8 @@ RCT_EXPORT_MODULE()
|
||||
- (void)clearAllData
|
||||
{
|
||||
dispatch_async(RCTGetMethodQueue(), ^{
|
||||
_manifest = [NSMutableDictionary new];
|
||||
[_manifest removeAllObjects];
|
||||
[RCTGetCache() removeAllObjects];
|
||||
RCTDeleteStorageDirectory();
|
||||
});
|
||||
}
|
||||
@ -160,6 +178,7 @@ RCT_EXPORT_MODULE()
|
||||
+ (void)clearAllData
|
||||
{
|
||||
dispatch_async(RCTGetMethodQueue(), ^{
|
||||
[RCTGetCache() removeAllObjects];
|
||||
RCTDeleteStorageDirectory();
|
||||
});
|
||||
}
|
||||
@ -167,10 +186,11 @@ RCT_EXPORT_MODULE()
|
||||
- (void)invalidate
|
||||
{
|
||||
if (_clearOnInvalidate) {
|
||||
[RCTGetCache() removeAllObjects];
|
||||
RCTDeleteStorageDirectory();
|
||||
}
|
||||
_clearOnInvalidate = NO;
|
||||
_manifest = [NSMutableDictionary new];
|
||||
[_manifest removeAllObjects];
|
||||
_haveSetup = NO;
|
||||
}
|
||||
|
||||
@ -245,15 +265,25 @@ RCT_EXPORT_MODULE()
|
||||
|
||||
- (NSString *)_getValueForKey:(NSString *)key errorOut:(NSDictionary **)errorOut
|
||||
{
|
||||
id value = _manifest[key]; // nil means missing, null means there is a data file, else: NSString
|
||||
NSString *value = _manifest[key]; // nil means missing, null means there may be a data file, else: NSString
|
||||
if (value == (id)kCFNull) {
|
||||
NSString *filePath = [self _filePathForKey:key];
|
||||
value = RCTReadFile(filePath, key, errorOut);
|
||||
value = [RCTGetCache() objectForKey:key];
|
||||
if (!value) {
|
||||
NSString *filePath = [self _filePathForKey:key];
|
||||
value = RCTReadFile(filePath, key, errorOut);
|
||||
if (value) {
|
||||
[RCTGetCache() setObject:value forKey:key cost:value.length];
|
||||
} else {
|
||||
// file does not exist after all, so remove from manifest (no need to save
|
||||
// manifest immediately though, as cost of checking again next time is negligible)
|
||||
[_manifest removeObjectForKey:key];
|
||||
}
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
- (NSDictionary *)_writeEntry:(NSArray<NSString *> *)entry
|
||||
- (NSDictionary *)_writeEntry:(NSArray<NSString *> *)entry changedManifest:(BOOL *)changedManifest
|
||||
{
|
||||
if (entry.count != 2) {
|
||||
return RCTMakeAndLogError(@"Entries must be arrays of the form [key: string, value: string], got: ", entry, nil);
|
||||
@ -267,18 +297,22 @@ RCT_EXPORT_MODULE()
|
||||
NSString *filePath = [self _filePathForKey:key];
|
||||
NSError *error;
|
||||
if (value.length <= RCTInlineValueThreshold) {
|
||||
if (_manifest[key] && _manifest[key] != (id)kCFNull) {
|
||||
if (_manifest[key] == (id)kCFNull) {
|
||||
// If the value already existed but wasn't inlined, remove the old file.
|
||||
[[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
|
||||
[RCTGetCache() removeObjectForKey:key];
|
||||
}
|
||||
*changedManifest = YES;
|
||||
_manifest[key] = value;
|
||||
return nil;
|
||||
}
|
||||
[value writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:&error];
|
||||
[RCTGetCache() setObject:value forKey:key cost:value.length];
|
||||
if (error) {
|
||||
errorOut = RCTMakeError(@"Failed to write value.", error, @{@"key": key});
|
||||
} else {
|
||||
_manifest[key] = (id)kCFNull; // Mark existence of file with null, any other value is inline data.
|
||||
} else if (_manifest[key] != (id)kCFNull) {
|
||||
*changedManifest = YES;
|
||||
_manifest[key] = (id)kCFNull;
|
||||
}
|
||||
return errorOut;
|
||||
}
|
||||
@ -296,7 +330,9 @@ RCT_EXPORT_METHOD(multiGet:(NSStringArray *)keys
|
||||
NSMutableArray<NSDictionary *> *errors;
|
||||
NSMutableArray<NSArray<NSString *> *> *result = [[NSMutableArray alloc] initWithCapacity:keys.count];
|
||||
for (NSString *key in keys) {
|
||||
NSDictionary *keyError = [self _appendItemForKey:key toArray:result];
|
||||
id keyError;
|
||||
id value = [self _getValueForKey:key errorOut:&keyError];
|
||||
[result addObject:@[key, RCTNullIfNil(value)]];
|
||||
RCTAppendError(keyError, &errors);
|
||||
}
|
||||
callback(@[RCTNullIfNil(errors), result]);
|
||||
@ -310,12 +346,15 @@ RCT_EXPORT_METHOD(multiSet:(NSStringArrayArray *)kvPairs
|
||||
callback(@[@[errorOut]]);
|
||||
return;
|
||||
}
|
||||
BOOL changedManifest = NO;
|
||||
NSMutableArray<NSDictionary *> *errors;
|
||||
for (NSArray<NSString *> *entry in kvPairs) {
|
||||
NSDictionary *keyError = [self _writeEntry:entry];
|
||||
NSDictionary *keyError = [self _writeEntry:entry changedManifest:&changedManifest];
|
||||
RCTAppendError(keyError, &errors);
|
||||
}
|
||||
[self _writeManifest:&errors];
|
||||
if (changedManifest) {
|
||||
[self _writeManifest:&errors];
|
||||
}
|
||||
callback(@[RCTNullIfNil(errors)]);
|
||||
}
|
||||
|
||||
@ -327,13 +366,12 @@ RCT_EXPORT_METHOD(multiMerge:(NSStringArrayArray *)kvPairs
|
||||
callback(@[@[errorOut]]);
|
||||
return;
|
||||
}
|
||||
BOOL changedManifest = NO;
|
||||
NSMutableArray<NSDictionary *> *errors;
|
||||
for (__strong NSArray<NSString *> *entry in kvPairs) {
|
||||
NSDictionary *keyError;
|
||||
NSString *value = [self _getValueForKey:entry[0] errorOut:&keyError];
|
||||
if (keyError) {
|
||||
RCTAppendError(keyError, &errors);
|
||||
} else {
|
||||
if (!keyError) {
|
||||
if (value) {
|
||||
NSError *jsonError;
|
||||
NSMutableDictionary *mergedVal = RCTJSONParseMutable(value, &jsonError);
|
||||
@ -345,12 +383,14 @@ RCT_EXPORT_METHOD(multiMerge:(NSStringArrayArray *)kvPairs
|
||||
}
|
||||
}
|
||||
if (!keyError) {
|
||||
keyError = [self _writeEntry:entry];
|
||||
keyError = [self _writeEntry:entry changedManifest:&changedManifest];
|
||||
}
|
||||
RCTAppendError(keyError, &errors);
|
||||
}
|
||||
RCTAppendError(keyError, &errors);
|
||||
}
|
||||
if (changedManifest) {
|
||||
[self _writeManifest:&errors];
|
||||
}
|
||||
[self _writeManifest:&errors];
|
||||
callback(@[RCTNullIfNil(errors)]);
|
||||
}
|
||||
|
||||
@ -363,22 +403,34 @@ RCT_EXPORT_METHOD(multiRemove:(NSStringArray *)keys
|
||||
return;
|
||||
}
|
||||
NSMutableArray<NSDictionary *> *errors;
|
||||
BOOL changedManifest = NO;
|
||||
for (NSString *key in keys) {
|
||||
NSDictionary *keyError = RCTErrorForKey(key);
|
||||
if (!keyError) {
|
||||
NSString *filePath = [self _filePathForKey:key];
|
||||
[[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
|
||||
[_manifest removeObjectForKey:key];
|
||||
if (_manifest[key] == (id)kCFNull) {
|
||||
NSString *filePath = [self _filePathForKey:key];
|
||||
[[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
|
||||
[RCTGetCache() removeObjectForKey:key];
|
||||
// remove the key from manifest, but no need to mark as changed just for
|
||||
// this, as the cost of checking again next time is negligible.
|
||||
[_manifest removeObjectForKey:key];
|
||||
} else if (_manifest[key]) {
|
||||
changedManifest = YES;
|
||||
[_manifest removeObjectForKey:key];
|
||||
}
|
||||
}
|
||||
RCTAppendError(keyError, &errors);
|
||||
}
|
||||
[self _writeManifest:&errors];
|
||||
if (changedManifest) {
|
||||
[self _writeManifest:&errors];
|
||||
}
|
||||
callback(@[RCTNullIfNil(errors)]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(clear:(RCTResponseSenderBlock)callback)
|
||||
{
|
||||
_manifest = [NSMutableDictionary new];
|
||||
[_manifest removeAllObjects];
|
||||
[RCTGetCache() removeAllObjects];
|
||||
NSDictionary *error = RCTDeleteStorageDirectory();
|
||||
callback(@[RCTNullIfNil(error)]);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user