EGOCache簡介
EGOCache is a simple, thread-safe key value cache store. It has native support for NSString, UI/NSImage, and NSData, but can store anything that implements <NSCoding>. All cached items expire after the timeout, which by default, is one day.
翻譯過來就是:EGOCache一個簡單、線程安全的基於 key-value 的緩存框架,原生支持NSString、UI/NSImage、和NSData,也支持儲存任何實現協議的類,可以設定緩存過期時間,默認是1天。
EGOCache只有一個類,EGOCache.h和EGOCache.m兩個文件。用法也比較容易掌握,仔細研究一下EGOCache.h的方法,很快就可以上手。
EGOCache只提供了磁盤緩存,沒有提供內存緩存。同時,也提供了清理緩存的方法:
復制代碼 代碼如下:
- (void)clearCache;
EGOCache還提供了判斷緩存是否存在的方法:
復制代碼 代碼如下:
- (BOOL)hasCacheForKey:(NSString* __nonnull)key;
通過Cocoapods直接加入項目
直接在你的項目的Podfile加入下面一行:
復制代碼 代碼如下:
pod 'EGOCache'
然後執行:
復制代碼 代碼如下:
$ pod update
EGOCache用法
用EGOCache緩存NSString
存儲:
復制代碼 代碼如下:
NSString *saveString = @"把我保存起來吧";
[[EGOCache globalCache] setString:saveString forKey:[NSString stringWithFormat:@"EGOImageLoader-%lu", (unsigned long)[saveString hash]] withTimeoutInterval:24*60*60];
讀取:
復制代碼 代碼如下:
NSString *getSaveString = [[EGOCache globalCache] stringForKey:[NSString stringWithFormat:@"EGOImageLoader-%lu", (unsigned long)[@"SaveString" hash]]];
是不是感覺跟NSDictionary很相似,確實,前面我們說了EGOCache是基於key-value 的緩存框架。
用EGOCache緩存UIImage
存儲:
復制代碼 代碼如下:
UIImage *saveImage = [UIImage imageNamed:@"iOSDevTip"];
[[EGOCache globalCache] setImage:saveImage forKey:[NSString stringWithFormat:@"EGOImageLoader-%lu", (unsigned long)[@"SaveImage" hash]] withTimeoutInterval:24*60*60];
讀取:
復制代碼 代碼如下:
UIImage *getSaveImage = [[EGOCache globalCache] imageForKey:[NSString stringWithFormat:@"EGOImageLoader-%lu", (unsigned long)[@"SaveImage" hash]]];
用EGOCache緩存NSData
存儲:
復制代碼 代碼如下:
NSData *saveData = [NSData data];
[[EGOCache globalCache] setData:saveData forKey:[NSString stringWithFormat:@"EGOImageLoader-%lu", (unsigned long)[@"SaveData" hash]] withTimeoutInterval:24*60*60];
讀取:
復制代碼 代碼如下:
UIImage *getSaveData = [[EGOCache globalCache] dataForKey:[NSString stringWithFormat:@"EGOImageLoader-%lu", (unsigned long)[@"SaveData" hash]]];
EGOCache如何檢測緩存時間過期
EGOCache可以設定緩存過期時間,默認是1天。查看了一下EGOCache源碼,設置默認時間:
復制代碼 代碼如下:
[self setDefaultTimeoutInterval:86400];
//86400 = 24 * 60 * 60 剛好是一天時間。
EGOCache為什麼要提供設定緩存過期時間呢?或者說設定緩存過期時間有什麼好處呢?我覺得最大的好處就是可以定時清除緩存。可以設置某一項的緩存時間,很方便管理緩存。
那麼問題來了:
帶著這兩個問題,我們來繼續分析。
你會怎麼實現
記得在公司裡,老板經常會舉這樣的例子:
某某同志,剛來我們公司的時候,遇到問題就知道抱怨。從來不知道去思考怎麼解決,只知道把問題拋給領導。工作半年下來,成長了很多。現在碰到問題,不僅把問題拋出來,而且還提供了自己的解決方案...
類似的例子,相信大家都聽過。同樣,既然前面我們提出這兩個問題,我們也先來思考一下,如果我們來做該怎麼解決?
如果讓我來寫的話,我腦海裡初步實現方法有幾個:
為了小小的緩存時間,就用定時器輪詢,顯然是資源浪費
跟方法1差不多。
每次讀取的時候判斷是否過期,如果一直不讀取,app的緩存會越來越大,也不可取。
這些方法都被排除了,還有好的方法嗎?繼續往下看:
EGOCache是怎麼實現的?
仔細查看EGOCache源碼,發現在initWithCacheDirectory:方法裡,每次初始化EGOCache實例對象的時,會遍歷一遍plist文件中所有已存在的緩存項,拿每個緩存項的時間和當前時間作比較,緩存項過期時間早於當前時間,則刪除對應緩存文件,並刪除 plist 文件中對應 key 的記錄。
具體實現代碼如下:
讀取緩存項信息
復制代碼 代碼如下:
_cacheInfo = [[NSDictionary dictionaryWithContentsOfFile:cachePathForKey(_directory, @"EGOCache.plist")] mutableCopy];
if(!_cacheInfo) {
_cacheInfo = [[NSMutableDictionary alloc] init];
}
獲取當前時間的NSTimeInterval
復制代碼 代碼如下:
NSTimeInterval now = [[NSDate date] timeIntervalSinceReferenceDate];
聲明removedKeys保存過期的緩存項對應的key
復制代碼 代碼如下:
NSMutableArray* removedKeys = [[NSMutableArray alloc] init];
遍歷緩存項信息並判斷緩存時間
復制代碼 代碼如下:
for(NSString* key in _cacheInfo) {
//判斷緩存項過期時間是否早於當前時間
if([_cacheInfo[key] timeIntervalSinceReferenceDate] <= now) {
//如果緩存項過期時間早於當前時間,移除緩存項
[[NSFileManager defaultManager] removeItemAtPath:cachePathForKey(_directory, key) error:NULL];
//把過期的緩存項對於的key保存到removedKeys裡面
[removedKeys addObject:key];
}
}
刪除過期緩存項對於的key
復制代碼 代碼如下:
[_cacheInfo removeObjectsForKeys:removedKeys];
看到這些,是不是覺得人家思路特牛叉,反正,我是覺得這個作者不簡單。到這一步就解決了嗎?
EGOCache還做了什麼?
細心的童鞋會發現:EGOCache是個單列類,也就是說整個程序應用周期只初始化一次。
復制代碼 代碼如下:
+ (instancetype)globalCache {
static id instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[[self class] alloc] init];
});
return instance;
}
每次初始化的時候去判斷了緩存項是否過期,這樣做非常正確。思考一個場景:
用戶打開app,EGOCache被初始化,並判斷了緩存項是否過期。
如果剛好有一些緩存項在EGOCache被初始化之後過期。這個時候我們依然可以讀到這個緩存項。這就不對了。
繼續分析EGOCache源碼發現,EGOCache在讀取一個緩存項的時候,先判斷緩存項是否存在,然後讀取緩存項(注意:是讀取EGOCache初始化的時候沒有過期的緩存項,並沒有說現在沒有過期),最後去判斷讀取到的緩存項跟當前時間相比是否過期.
具體實現如下:
復制代碼 代碼如下:
- (BOOL)hasCacheForKey:(NSString*)key {
//讀取EGOCache初始化的時候沒有過期的緩存項
NSDate* date = [self dateForKey:key];
if(date == nil) return NO;
//判斷讀取到的緩存項當前是否過期
if([date timeIntervalSinceReferenceDate] < CFAbsoluteTimeGetCurrent()) return NO;
return [[NSFileManager defaultManager] fileExistsAtPath:cachePathForKey(_directory, key)];
}
- (NSDate*)dateForKey:(NSString*)key {
__block NSDate* date = nil;
dispatch_sync(_frozenCacheInfoQueue, ^{
date = (self.frozenCacheInfo)[key];
});
return date;
}
EGOCache檢測緩存時間過期的思路值得學習,以後遇到類似場景,完全可以借鑒。