本文假設(shè)你對weex有基本的了解?。ū疚牡哪康氖侵v解iOS的一種緩存方案實(shí)現(xiàn),weex的部分不懂并無大礙。如果不關(guān)心,可以直接從iOS實(shí)現(xiàn)的部分開始閱讀)
weex Module的簡單介紹
言歸正題,先從官方文檔了解Storage的使用方式。
我們發(fā)現(xiàn),客戶端主要提供了4個API:
- setItem(key, value, callback)
- getItem(key, callback)
- removeItem(key, callback)
- length(callback)
下面,我們逐一分析他們的具體實(shí)現(xiàn)。
Storage 實(shí)現(xiàn)
1. 存儲數(shù)據(jù)
先上代碼:
- (void)setObject:(NSString *)obj forKey:(NSString *)key persistent:(BOOL)persistent callback:(WXModuleCallback)callback {
NSString *filePath = [WXStorageModule filePathForKey:key];
if (obj.length <= WXStorageLineLimit) {
if ([WXStorageNullValue isEqualToString:self.memory[key]]) {
[[WXUtility globalCache] removeObjectForKey:key];
[[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
}
self.memory[key] = obj;
NSDictionary *dict = [self.memory copy];
[self write:dict toFilePath:[WXStorageModule filePath]];
[self setInfo:@{@"persistent":@(persistent),@"size":@(obj.length)} ForKey:key];
[self updateIndexForKey:key];
[self checkStorageLimit];
if (callback) {
callback(@{@"result":@"success"});
}
return;
}
[[WXUtility globalCache] setObject:obj forKey:key cost:obj.length];
if (![WXStorageNullValue isEqualToString:self.memory[key]]) {
self.memory[key] = WXStorageNullValue;
NSDictionary *dict = [self.memory copy];
[self write:dict toFilePath:[WXStorageModule filePath]];
}
dispatch_async([WXStorageModule storageQueue], ^{
[obj writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:NULL];
});
[self setInfo:@{@"persistent":@(persistent),@"size":@(obj.length)} ForKey:key];
[self updateIndexForKey:key];
[self checkStorageLimit];
if (callback) {
callback(@{@"result":@"success"});
}
}
代碼解析:
- 首先,在這個方法之前,檢驗(yàn)了key的類型。只能是NSString或NSNumber,且不能為空,并去除了空格和回車字符。
- 其次,根據(jù)key的
name在沙盒的~/Document目錄下生成一個wxStorage目錄。文件名是name的MD5值。wxStorage目錄下存放數(shù)據(jù)內(nèi)容時會生成3個文件,分別是:wxStorage.plist,wxStorage.info.plist,wxStorage.index.plist。第一個存放數(shù)據(jù)鍵值對;第二個存放數(shù)據(jù)的額外信息,如存放時間、數(shù)據(jù)長度、索引信息等;第三個存放key值,用于遍歷。 - 如果data的內(nèi)容length小于Limit(weex指定為1024byte),則將該key/value直接寫入
memory字典。memory是weex自身實(shí)現(xiàn)的一個線程安全型的字典。隨后,方法會依次把數(shù)據(jù)寫入上一步所說的3個plist文件中。檢查storage是否超出Limit限制。如果是,則刪除部分內(nèi)容。 - 如果data的內(nèi)容length大于Limit,先將該key/value存放于
globalCache中。這是一個基于NSCache實(shí)現(xiàn)的全局緩存,目的是為了數(shù)據(jù)能在內(nèi)存中存儲,被高效地使用。存儲的步驟和上面的“小數(shù)據(jù)”一樣。不同的是,大數(shù)據(jù)不是直接存放在plist中。plist中存放了一個假數(shù)據(jù),并和之前一樣同步的被寫入plist文件。而真數(shù)據(jù)是額外開了一個隊(duì)列,異步寫入的。filePath即是當(dāng)前目錄,文件名為key的MD5值。
總結(jié)一下:數(shù)據(jù)先是存放在了一個線程安全的字典中;以1024byte為分界線,如果是“小數(shù)據(jù)”,則同步寫入一個管理文件;如果是“大數(shù)據(jù)”,則異步寫入文件,每個數(shù)據(jù)單獨(dú)存放為一個文件。
2. 獲取數(shù)據(jù)
- (void)getItem:(NSString *)key callback:(WXModuleCallback)callback
{
if ([self checkInput:key]) {
if (callback) {
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]) {
if (callback) {
callback(@{@"result":@"failed",@"data":@"invalid_param"});
}
return ;
}
NSString *value = [self.memory objectForKey:key];
if ([WXStorageNullValue isEqualToString:value]) {
value = [[WXUtility globalCache] objectForKey:key];
if (!value) {
NSString *filePath = [WXStorageModule filePathForKey:key];
NSString *contents = [WXUtility stringWithContentsOfFile:filePath];
if (contents) {
[[WXUtility globalCache] setObject:contents forKey:key cost:contents.length];
value = contents;
}
}
}
if (!value) {
[self executeRemoveItem:key];
if (callback) {
callback(@{@"result":@"failed",@"data":@"undefined"});
}
return;
}
[self updateTimestampForKey:key];
[self updateIndexForKey:key];
if (callback) {
callback(@{@"result":@"success",@"data":value});
}
}
了解了如何存儲,獲取就相對簡單了。依次,先從memory字典中找,然后去globalCache中找,最后去filePath下找;如果都沒找到,則移除該key,并把文件中和該key相關(guān)的內(nèi)容都刪除了。
總結(jié)
對比YYCache的實(shí)現(xiàn),weex的storage模塊實(shí)現(xiàn)比較簡單,條理清晰。在簡單的數(shù)據(jù)緩存上完全夠用了。