Skip to content

Commit

Permalink
Merge pull request #939 from pythons/ios-feature-localstorage
Browse files Browse the repository at this point in the history
iOS feature local storage - Using asynchronous queue to improve the writing performance
  • Loading branch information
boboning committed Aug 8, 2016
2 parents 2b41423 + 228931a commit 3038dda
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 101 deletions.
8 changes: 0 additions & 8 deletions ios/sdk/WeexSDK.xcodeproj/project.pbxproj
Expand Up @@ -10,8 +10,6 @@
1D3000F41D41F5BE004F3B4F /* libicucore.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 7469869B1C4DEAC20054A57E /* libicucore.tbd */; };
1D3000F51D41FAEE004F3B4F /* SRWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A27E7D81C3E360B00D7A552 /* SRWebSocket.m */; };
1D3000F71D41FB22004F3B4F /* WXStorageTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D3000F61D41FB22004F3B4F /* WXStorageTests.m */; };
1D467A281D50459B0007AD9E /* WXUtility+Hash.h in Headers */ = {isa = PBXBuildFile; fileRef = 1D467A261D50459B0007AD9E /* WXUtility+Hash.h */; };
1D467A291D50459B0007AD9E /* WXUtility+Hash.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D467A271D50459B0007AD9E /* WXUtility+Hash.m */; };
2A1F57B71C75C6A600B58017 /* WXTextInputComponent.h in Headers */ = {isa = PBXBuildFile; fileRef = 2A1F57B51C75C6A600B58017 /* WXTextInputComponent.h */; };
2A1F57B81C75C6A600B58017 /* WXTextInputComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A1F57B61C75C6A600B58017 /* WXTextInputComponent.m */; };
2A42AF881C23B33E00818EA6 /* WeexSDK_MTL.h in Copy Files */ = {isa = PBXBuildFile; fileRef = 2A42AF871C23B33E00818EA6 /* WeexSDK_MTL.h */; };
Expand Down Expand Up @@ -201,8 +199,6 @@

/* Begin PBXFileReference section */
1D3000F61D41FB22004F3B4F /* WXStorageTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WXStorageTests.m; sourceTree = "<group>"; };
1D467A261D50459B0007AD9E /* WXUtility+Hash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "WXUtility+Hash.h"; sourceTree = "<group>"; };
1D467A271D50459B0007AD9E /* WXUtility+Hash.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "WXUtility+Hash.m"; sourceTree = "<group>"; };
2A1F57B51C75C6A600B58017 /* WXTextInputComponent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXTextInputComponent.h; sourceTree = "<group>"; };
2A1F57B61C75C6A600B58017 /* WXTextInputComponent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WXTextInputComponent.m; sourceTree = "<group>"; };
2A27E7D71C3E360B00D7A552 /* SRWebSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SRWebSocket.h; path = dependency/SRWebSocket.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -687,8 +683,6 @@
7461F8A71CFC33A800F62D44 /* WXThreadSafeMutableArray.m */,
74896F2E1D1AC79400D1D593 /* NSObject+WXSwizzle.h */,
74896F2F1D1AC79400D1D593 /* NSObject+WXSwizzle.m */,
1D467A261D50459B0007AD9E /* WXUtility+Hash.h */,
1D467A271D50459B0007AD9E /* WXUtility+Hash.m */,
);
path = Utility;
sourceTree = "<group>";
Expand Down Expand Up @@ -786,7 +780,6 @@
74CC7A201C2BF9DC00829368 /* WXListComponent.h in Headers */,
74FD6E041C7C0E9600DBEB6D /* WXScrollerProtocol.h in Headers */,
77D161201C02DDB40010B15B /* WXSDKEngine.h in Headers */,
1D467A281D50459B0007AD9E /* WXUtility+Hash.h in Headers */,
745ED2DA1C5F2C7E002DB5A8 /* WXView.h in Headers */,
59D3CA411CF9ED57008835DC /* Layout.h in Headers */,
2AE5B7521CAB7DBD0082FDDB /* WXAComponent.h in Headers */,
Expand Down Expand Up @@ -1058,7 +1051,6 @@
77D161311C02DE4E0010B15B /* WXComponent.m in Sources */,
77E659DB1C07F594008B8775 /* WXDomModule.m in Sources */,
D3FC0DF81C508B2A002B9E31 /* WXTimerModule.m in Sources */,
1D467A291D50459B0007AD9E /* WXUtility+Hash.m in Sources */,
594C28921CF9E61A009793A4 /* WXAnimationModule.m in Sources */,
59A5961D1CB630F10012CD52 /* WXComponent+Navigation.m in Sources */,
77D161631C02ED790010B15B /* WXLog.m in Sources */,
Expand Down
211 changes: 119 additions & 92 deletions ios/sdk/WeexSDK/Sources/Module/WXStorageModule.m
Expand Up @@ -13,9 +13,10 @@
#import "WXUtility.h"

static NSString * const WXStorageDirectory = @"wxstorage";
static NSString * const WXStorageFileName = @"wxstorage.json";
static NSString * const WXStorageFileName = @"wxstorage.plist";
static NSUInteger const WXStorageLineLimit = 1024;
static NSString * const WXStorageThreadName = @"com.taobao.weex.storage";
static NSString * const WXStorageNullValue = @"#{eulaVlluNegarotSXW}";

@implementation WXStorageModule

Expand All @@ -41,135 +42,152 @@ - (void)getAllKeys:(WXModuleCallback)callback

- (void)getItem:(NSString *)key callback:(WXModuleCallback)callback
{
if ([key isKindOfClass:[NSString class]] == NO) {
callback(@{@"result":@"failed",@"data":@"key must a string!"});
if ([self checkInput:key]) {
callback(@{@"result":@"failed",@"data":@"key must a string or number!"}); // forgive my english
return;
}

if ([key isKindOfClass:[NSNumber class]]) {
key = [((NSNumber *)key) stringValue]; // oh no!
}

if ([WXUtility isBlankString:key]) {
callback(@{@"result":@"failed",@"data":@"invalid_param"});
return ;
}
NSString *value = [self.memory objectForKey:key];
if (value == (id)kCFNull) {
value = [[WXUtility globalCache] objectForKey:key];

__weak typeof(self) weakSelf = self;
dispatch_async([WXStorageModule storageQueue], ^{
WXStorageModule *strongSelf = weakSelf;
NSString *value = [strongSelf.memory objectForKey:key];
if ([WXStorageNullValue isEqualToString:value]) {
value = [[WXUtility globalCache] objectForKey:key];
if (!value) {
dispatch_async([WXStorageModule storageQueue], ^{
WXStorageModule *strongSelf = weakSelf;
NSString *filePath = [WXStorageModule filePathForKey:key];
NSString *contents = [WXUtility stringWithContentsOfFile:filePath];
if (contents) {
[[WXUtility globalCache] setObject:contents forKey:key cost:contents.length];
callback(@{@"result":@"success",@"data":contents});
} else {
[strongSelf.memory removeObjectForKey:key];
callback(@{@"result":@"failed"});
}
});
return;
}
}
if (!value) {
dispatch_async([WXStorageModule storageQueue], ^{
NSString *filePath = [WXStorageModule filePathForKey:key];
NSString *contents = [WXUtility stringWithContentsOfFile:filePath];
if (contents) {
[[WXUtility globalCache] setObject:contents forKey:key cost:contents.length];
callback(@{@"result":@"success",@"data":contents});
} else {
[self.memory removeObjectForKey:key];
callback(@{@"result":@"failed"});
}
});
callback(@{@"result":@"failed"});
return ;
}
}
if (!value) {
callback(@{@"result":@"failed"});
return ;
}
callback(@{@"result":@"success",@"data":value});
callback(@{@"result":@"success",@"data":value});
});
}

- (void)setItem:(NSString *)key value:(NSString *)value callback:(WXModuleCallback)callback
{
if ([key isKindOfClass:[NSString class]] == NO) {
callback(@{@"result":@"failed",@"data":@"key must a string!"});
if ([self checkInput:key]) {
callback(@{@"result":@"failed",@"data":@"key must a string or number!"});
return;
}
if ([value isKindOfClass:[NSString class]] == NO) {
callback(@{@"result":@"failed",@"data":@"value must a string!"});
if ([self checkInput:value]) {
callback(@{@"result":@"failed",@"data":@"value must a string or number!"});
return;
}

if ([key isKindOfClass:[NSNumber class]]) {
key = [((NSNumber *)key) stringValue];
}

if ([value isKindOfClass:[NSNumber class]]) {
value = [((NSNumber *)value) stringValue];
}

if ([WXUtility isBlankString:key]) {
callback(@{@"result":@"failed",@"data":@"invalid_param"});
return ;
}
[self setObject:value forKey:key];
callback(@{@"result":@"success"});
[self setObject:value forKey:key callback:callback];
}

- (void)removeItem:(NSString *)key callback:(WXModuleCallback)callback
{
if ([key isKindOfClass:[NSString class]] == NO) {
callback(@{@"result":@"failed",@"data":@"key must a string!"});
if ([self checkInput:key]) {
callback(@{@"result":@"failed",@"data":@"key must a string or number!"});
return;
}
if (self.memory[key] == (id)kCFNull) {
[self.memory removeObjectForKey:key];
dispatch_async([WXStorageModule storageQueue], ^{
NSString *filePath = [WXStorageModule filePathForKey:key];
[[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
[[WXUtility globalCache] removeObjectForKey:key];
});
} else if (self.memory[key]) {
[self.memory removeObjectForKey:key];
NSDictionary *dict = [self.memory copy];
__weak typeof(self) weakSelf = self;
dispatch_async([WXStorageModule storageQueue], ^{

if ([key isKindOfClass:[NSNumber class]]) {
key = [((NSNumber *)key) stringValue];
}

if ([WXUtility isBlankString:key]) {
callback(@{@"result":@"failed",@"data":@"invalid_param"});
return ;
}
__weak typeof(self) weakSelf = self;
dispatch_async([WXStorageModule storageQueue], ^{
WXStorageModule *strongSelf = weakSelf;
if ([WXStorageNullValue isEqualToString:strongSelf.memory[key]]) {
[strongSelf.memory removeObjectForKey:key];
dispatch_async([WXStorageModule storageQueue], ^{
NSString *filePath = [WXStorageModule filePathForKey:key];
[[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
[[WXUtility globalCache] removeObjectForKey:key];
});
} else if (strongSelf.memory[key]) {
[strongSelf.memory removeObjectForKey:key];
WXStorageModule *strongSelf = weakSelf;
NSDictionary *dict = [strongSelf.memory copy];
[strongSelf write:dict toFilePath:[WXStorageModule filePath]];
});
}
callback(@{@"result":@"success"});
}else{
callback(@{@"result":@"failed"});
return ;
}
callback(@{@"result":@"success"});
});
}

#pragma mark - Utils

- (void)setObject:(NSString *)obj forKey:(NSString *)key{

NSString *filePath = [WXStorageModule filePathForKey:key];

if (obj.length <= WXStorageLineLimit) {
if (self.memory[key] == (id)kCFNull) {
[[WXUtility globalCache] removeObjectForKey:key];
[[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
- (void)setObject:(NSString *)obj forKey:(NSString *)key callback:(WXModuleCallback)callback{
__weak typeof(self) weakSelf = self;
dispatch_async([WXStorageModule storageQueue], ^{
WXStorageModule *strongSelf = weakSelf;
NSString *filePath = [WXStorageModule filePathForKey:key];
if (obj.length <= WXStorageLineLimit) {
if ([WXStorageNullValue isEqualToString:strongSelf.memory[key]]) {
[[WXUtility globalCache] removeObjectForKey:key];
[[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
}
strongSelf.memory[key] = obj;
NSDictionary *dict = [strongSelf.memory copy];
[strongSelf write:dict toFilePath:[WXStorageModule filePath]];
callback(@{@"result":@"success"});
return;
}
self.memory[key] = obj;
__weak typeof(self) weakSelf = self;
NSDictionary *dict = [self.memory copy];
dispatch_async([WXStorageModule storageQueue], ^{

[[WXUtility globalCache] setObject:obj forKey:key cost:obj.length];

if (![WXStorageNullValue isEqualToString:strongSelf.memory[key]]) {
strongSelf.memory[key] = WXStorageNullValue;
WXStorageModule *strongSelf = weakSelf;
NSDictionary *dict = [strongSelf.memory copy];
[strongSelf write:dict toFilePath:[WXStorageModule filePath]];
});
}

return;
}

[[WXUtility globalCache] setObject:obj forKey:key cost:obj.length];

dispatch_async([WXStorageModule storageQueue], ^{
[obj writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:NULL];
});

if (self.memory[key] != (id)kCFNull) {
self.memory[key] = (id)kCFNull;
__weak typeof(self) weakSelf = self;
NSDictionary *dict = [self.memory copy];
dispatch_async([WXStorageModule storageQueue], ^{
WXStorageModule *strongSelf = weakSelf;
[strongSelf write:dict toFilePath:[WXStorageModule filePath]];
[obj writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:NULL];
});
}

callback(@{@"result":@"success"});
});
}

- (void)write:(NSDictionary *)dict toFilePath:(NSString *)filePath{
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dict
options:(NSJSONWritingOptions)0
error:&error];
if (error) {
return ;
}
NSString *contents = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];

[contents writeToFile:filePath
atomically:YES
encoding:NSUTF8StringEncoding
error:NULL];

[dict writeToFile:filePath atomically:YES];
}

+ (NSString *)filePathForKey:(NSString *)key
Expand Down Expand Up @@ -224,13 +242,18 @@ + (dispatch_queue_t)storageQueue {
dispatch_once(&onceToken, ^{
[WXStorageModule setupDirectory];

NSString *contents = [WXUtility stringWithContentsOfFile:[WXStorageModule filePath]];
if (contents) {
memory = [[WXThreadSafeMutableDictionary alloc] initWithDictionary:[WXUtility objectFromJSON:contents]];
if ([[NSFileManager defaultManager] fileExistsAtPath:[WXStorageModule filePath]]) {
NSDictionary *contents = [NSDictionary dictionaryWithContentsOfFile:[WXStorageModule filePath]];
if (contents) {
memory = [[WXThreadSafeMutableDictionary alloc] initWithDictionary:contents];
}
}
if (!memory) {
memory = [WXThreadSafeMutableDictionary new];
}
// [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidReceiveMemoryWarningNotification object:nil queue:nil usingBlock:^(__unused NSNotification *note) {
// [memory removeAllObjects];
// }];
});
return memory;
}
Expand All @@ -239,5 +262,9 @@ + (dispatch_queue_t)storageQueue {
return [WXStorageModule memory];
}

- (BOOL)checkInput:(id)input{
return !([input isKindOfClass:[NSString class]] || [input isKindOfClass:[NSNumber class]]);
}

@end

14 changes: 14 additions & 0 deletions ios/sdk/WeexSDK/Sources/Utility/WXThreadSafeMutableDictionary.m
Expand Up @@ -118,4 +118,18 @@ - (void)removeObjectForKey:(id)aKey
});
}

- (void)removeAllObjects{
dispatch_barrier_async(_queue, ^{
[_dict removeAllObjects];
});
}

- (id)copy{
__block id copyInstance;
dispatch_sync(_queue, ^{
copyInstance = [_dict copy];
});
return copyInstance;
}

@end
2 changes: 1 addition & 1 deletion ios/sdk/WeexSDK/Sources/Utility/WXUtility.h
Expand Up @@ -295,5 +295,5 @@ CGPoint WXPixelPointResize(CGPoint value);
* @abstract Returns md5 string.
*
*/
+ (NSString *)md5:(NSString *)string;
+ (NSString *_Nullable)md5:(NSString *_Nullable)string;
@end

0 comments on commit 3038dda

Please sign in to comment.