Backed out D2540626

Reviewed By: jingc

Differential Revision: D2590392

fb-gh-sync-id: 2893a4ee6ddd0de12803560b355cee6f1b9d43e6
This commit is contained in:
Nick Lockwood 2015-10-28 11:43:47 -07:00 committed by facebook-github-bot-9
parent 84a0874f0d
commit b59f250214
1 changed files with 46 additions and 66 deletions

View File

@ -19,7 +19,7 @@
static NSString *const RCTStorageDirectory = @"RCTAsyncLocalStorage_V1"; static NSString *const RCTStorageDirectory = @"RCTAsyncLocalStorage_V1";
static NSString *const RCTManifestFileName = @"manifest.json"; static NSString *const RCTManifestFileName = @"manifest.json";
static const NSUInteger RCTInlineValueThreshold = 1024; static const NSUInteger RCTInlineValueThreshold = 100;
#pragma mark - Static helper functions #pragma mark - Static helper functions
@ -83,32 +83,31 @@ static NSString *RCTGetManifestFilePath()
} }
// Only merges objects - all other types are just clobbered (including arrays) // Only merges objects - all other types are just clobbered (including arrays)
// returns YES if destination was modified, or NO if no changes were needed. static void RCTMergeRecursive(NSMutableDictionary *destination, NSDictionary *source)
static BOOL RCTMergeRecursive(NSMutableDictionary *destination, NSDictionary *source)
{ {
BOOL modified = NO;
for (NSString *key in source) { for (NSString *key in source) {
id sourceValue = source[key]; id sourceValue = source[key];
id destinationValue = destination[key];
if ([sourceValue isKindOfClass:[NSDictionary class]]) { if ([sourceValue isKindOfClass:[NSDictionary class]]) {
if ([destinationValue isKindOfClass:[NSDictionary class]]) { id destinationValue = destination[key];
if ([destinationValue classForCoder] != [NSMutableDictionary class]) { NSMutableDictionary *nestedDestination;
destinationValue = [destinationValue mutableCopy]; if ([destinationValue classForCoder] == [NSMutableDictionary class]) {
} nestedDestination = destinationValue;
if (RCTMergeRecursive(destinationValue, sourceValue)) {
destination[key] = destinationValue;
modified = YES;
}
} else { } else {
destination[key] = [sourceValue copy]; if ([destinationValue isKindOfClass:[NSDictionary class]]) {
modified = YES; // Ideally we wouldn't eagerly copy here...
nestedDestination = [destinationValue mutableCopy];
} else {
destination[key] = [sourceValue copy];
}
} }
} else if (![source isEqual:destinationValue]) { if (nestedDestination) {
destination[key] = [sourceValue copy]; RCTMergeRecursive(nestedDestination, sourceValue);
modified = YES; destination[key] = nestedDestination;
}
} else {
destination[key] = sourceValue;
} }
} }
return modified;
} }
static dispatch_queue_t RCTGetMethodQueue() static dispatch_queue_t RCTGetMethodQueue()
@ -122,23 +121,6 @@ static dispatch_queue_t RCTGetMethodQueue()
return queue; 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 BOOL RCTHasCreatedStorageDirectory = NO;
static NSError *RCTDeleteStorageDirectory() static NSError *RCTDeleteStorageDirectory()
{ {
@ -168,7 +150,6 @@ RCT_EXPORT_MODULE()
+ (void)clearAllData + (void)clearAllData
{ {
[RCTGetCache() removeAllObjects];
dispatch_async(RCTGetMethodQueue(), ^{ dispatch_async(RCTGetMethodQueue(), ^{
RCTDeleteStorageDirectory(); RCTDeleteStorageDirectory();
}); });
@ -177,7 +158,6 @@ RCT_EXPORT_MODULE()
- (void)invalidate - (void)invalidate
{ {
if (_clearOnInvalidate) { if (_clearOnInvalidate) {
[RCTGetCache() removeAllObjects];
RCTDeleteStorageDirectory(); RCTDeleteStorageDirectory();
} }
_clearOnInvalidate = NO; _clearOnInvalidate = NO;
@ -219,7 +199,7 @@ RCT_EXPORT_MODULE()
if (!_haveSetup) { if (!_haveSetup) {
NSDictionary *errorOut; NSDictionary *errorOut;
NSString *serialized = RCTReadFile(RCTGetManifestFilePath(), nil, &errorOut); NSString *serialized = RCTReadFile(RCTGetManifestFilePath(), nil, &errorOut);
_manifest = serialized ? RCTJSONParseMutable(serialized, &error) : [NSMutableDictionary new]; _manifest = serialized ? [RCTJSONParse(serialized, &error) mutableCopy] : [NSMutableDictionary new];
if (error) { if (error) {
RCTLogWarn(@"Failed to parse manifest - creating new one.\n\n%@", error); RCTLogWarn(@"Failed to parse manifest - creating new one.\n\n%@", error);
_manifest = [NSMutableDictionary new]; _manifest = [NSMutableDictionary new];
@ -242,16 +222,23 @@ RCT_EXPORT_MODULE()
return errorOut; return errorOut;
} }
- (id)_appendItemForKey:(NSString *)key toArray:(NSMutableArray *)result
{
id errorOut = RCTErrorForKey(key);
if (errorOut) {
return errorOut;
}
id value = [self _getValueForKey:key errorOut:&errorOut];
[result addObject:@[key, RCTNullIfNil(value)]]; // Insert null if missing or failure.
return errorOut;
}
- (NSString *)_getValueForKey:(NSString *)key errorOut:(NSDictionary **)errorOut - (NSString *)_getValueForKey:(NSString *)key errorOut:(NSDictionary **)errorOut
{ {
NSString *value = _manifest[key]; // nil means missing, null means there is a data file, anything else is an inline value. id value = _manifest[key]; // nil means missing, null means there is a data file, anything else is an inline value.
if (value == (id)kCFNull) { if (value == (id)kCFNull) {
value = [RCTGetCache() objectForKey:key]; NSString *filePath = [self _filePathForKey:key];
if (!value) { value = RCTReadFile(filePath, key, errorOut);
NSString *filePath = [self _filePathForKey:key];
value = RCTReadFile(filePath, key, errorOut);
[RCTGetCache() setObject:value forKey:key cost:value.length];
}
} }
return value; return value;
} }
@ -276,13 +263,11 @@ RCT_EXPORT_MODULE()
if (_manifest[key] && _manifest[key] != (id)kCFNull) { if (_manifest[key] && _manifest[key] != (id)kCFNull) {
// If the value already existed but wasn't inlined, remove the old file. // If the value already existed but wasn't inlined, remove the old file.
[[NSFileManager defaultManager] removeItemAtPath:filePath error:nil]; [[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
[RCTGetCache() removeObjectForKey:key];
} }
_manifest[key] = value; _manifest[key] = value;
return nil; return nil;
} }
[value writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:&error]; [value writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:&error];
[RCTGetCache() setObject:value forKey:key cost:value.length];
if (error) { if (error) {
errorOut = RCTMakeError(@"Failed to write value.", error, @{@"key": key}); errorOut = RCTMakeError(@"Failed to write value.", error, @{@"key": key});
} else { } else {
@ -309,9 +294,7 @@ RCT_EXPORT_METHOD(multiGet:(NSArray *)keys
NSMutableArray *errors; NSMutableArray *errors;
NSMutableArray *result = [[NSMutableArray alloc] initWithCapacity:keys.count]; NSMutableArray *result = [[NSMutableArray alloc] initWithCapacity:keys.count];
for (NSString *key in keys) { for (NSString *key in keys) {
id keyError; id keyError = [self _appendItemForKey:key toArray:result];
id value = [self _getValueForKey:key errorOut:&keyError];
[result addObject:@[key, RCTNullIfNil(value)]];
RCTAppendError(keyError, &errors); RCTAppendError(keyError, &errors);
} }
callback(@[RCTNullIfNil(errors), result]); callback(@[RCTNullIfNil(errors), result]);
@ -348,18 +331,19 @@ RCT_EXPORT_METHOD(multiMerge:(NSArray *)kvPairs
for (__strong NSArray *entry in kvPairs) { for (__strong NSArray *entry in kvPairs) {
id keyError; id keyError;
NSString *value = [self _getValueForKey:entry[0] errorOut:&keyError]; NSString *value = [self _getValueForKey:entry[0] errorOut:&keyError];
if (!keyError) { if (keyError) {
RCTAppendError(keyError, &errors);
} else {
if (value) { if (value) {
NSMutableDictionary *mergedVal = RCTJSONParseMutable(value, &keyError); NSMutableDictionary *mergedVal = [RCTJSONParseMutable(value, &keyError) mutableCopy];
if (RCTMergeRecursive(mergedVal, RCTJSONParse(entry[1], &keyError))) { RCTMergeRecursive(mergedVal, RCTJSONParse(entry[1], &keyError));
entry = @[entry[0], RCTJSONStringify(mergedVal, &keyError)]; entry = @[entry[0], RCTJSONStringify(mergedVal, &keyError)];
keyError = [self _writeEntry:entry]; }
} if (!keyError) {
} else {
keyError = [self _writeEntry:entry]; keyError = [self _writeEntry:entry];
} }
RCTAppendError(keyError, &errors);
} }
RCTAppendError(keyError, &errors);
} }
[self _writeManifest:&errors]; [self _writeManifest:&errors];
if (callback) { if (callback) {
@ -379,11 +363,8 @@ RCT_EXPORT_METHOD(multiRemove:(NSArray *)keys
for (NSString *key in keys) { for (NSString *key in keys) {
id keyError = RCTErrorForKey(key); id keyError = RCTErrorForKey(key);
if (!keyError) { if (!keyError) {
if ( _manifest[key] == (id)kCFNull) { NSString *filePath = [self _filePathForKey:key];
NSString *filePath = [self _filePathForKey:key]; [[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
[[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
[RCTGetCache() removeObjectForKey:key];
}
[_manifest removeObjectForKey:key]; [_manifest removeObjectForKey:key];
} }
RCTAppendError(keyError, &errors); RCTAppendError(keyError, &errors);
@ -396,8 +377,7 @@ RCT_EXPORT_METHOD(multiRemove:(NSArray *)keys
RCT_EXPORT_METHOD(clear:(RCTResponseSenderBlock)callback) RCT_EXPORT_METHOD(clear:(RCTResponseSenderBlock)callback)
{ {
[_manifest removeAllObjects]; _manifest = [NSMutableDictionary new];
[RCTGetCache() removeAllObjects];
NSError *error = RCTDeleteStorageDirectory(); NSError *error = RCTDeleteStorageDirectory();
if (callback) { if (callback) {
callback(@[RCTNullIfNil(error)]); callback(@[RCTNullIfNil(error)]);