前言:
今年重點在於公司iOS架構的梳理工作,上周整理了http請求接口管理與解耦,接下來准備整理一下項目中的緩存處理,目前項目中使用的是PINCache,去年加入這個開源框架時並沒有對這個框架進行了解,導致現在同步方式異步方式的使用存在一定的混亂情況和錯誤使用現象。今天重新站在使用者的角度對這個再做一次了解,以避免在后期的使用中出現類似以往的問題。
關於緩存:
無論是Android還是IOS都會使用到緩存,緩存的設計方案也大致雷同(內存緩存+磁盤緩存),內存緩存方面Android采用LinkedHashMap,IOS采用NSDictionary,兩者都是基於Key-Value模型進行存儲,磁盤緩存方面Android采用寫文件,IOS這邊采用歸檔操作本質是也是寫文件。
關於PINCache
PINCache是Pinterest的程序員在Tumblr的TMCache基礎上發展而來的,TMCache已經不再維護,PINCache主要的改進是修復了dealock的bug,是一個快速,無死鎖的並行對象緩存,支持 iOS 和 OS X 系統。上面已經了解到PINCache分兩層緩存(內存緩存+磁盤緩存),內部實現主要有兩個類實現:PINMemoryCache、PINDiskCache,同時對兩者提供了同步異步調用方式,由於磁盤緩存采用的是歸檔操作,所以對自定義的對象必須實現NSCoding協議,PINCache除了可以按鍵取值、按鍵存值、按鍵刪值之外,還可以移除某個日期之前的緩存數據、刪除所有緩存、限制緩存大小,限制緩存對象的存活時間等。接下來看下具體使用方式。
PINCache使用
同步方式
//模擬數據 NSString *value=@"who is lcj"; //模擬一個key NSString *key=@"whoislcj"; //sync 同步方式 //寫入緩存 [[PINCache sharedCache] setObject:value forKey:key]; //判斷緩存是否存在 BOOL containsObject =[[PINCache sharedCache] containsObjectForKey:key]; NSLog(@"containsObject : %@", containsObject?@"YES":@"NO"); //根據key讀取數據 id vuale=[[PINCache sharedCache] objectForKey:key]; NSLog(@"value : %@",vuale); //根據key移除緩存 [[PINCache sharedCache]removeObjectForKey:key]; //移除所有緩存 [[PINCache sharedCache]removeAllObjects]; //根據截至日期 清除緩存 [[PINCache sharedCache]trimToDate:[NSDate date]];
異步方式
//模擬數據 NSString *value=@"who is lcj"; //模擬一個key NSString *key=@"whoislcj"; //async 異步方式 //寫入數據 [[PINCache sharedCache] setObject:value forKey:key block:^(PINCache * _Nonnull cache, NSString * _Nonnull key, id _Nullable object) { NSLog(@"value : %@",object); }]; //判斷緩存是否存在 [[PINCache sharedCache]containsObjectForKey:key block:^(BOOL containsObject) { NSLog(@"isContains : %@", containsObject?@"YES":@"NO"); }]; //根據key讀取數據 [[PINCache sharedCache]objectForKey:key block:^(PINCache * _Nonnull cache, NSString * _Nonnull key, id _Nullable object) { NSLog(@"value : %@",object); }]; //根據key移除緩存 [[PINCache sharedCache]removeObjectForKey:key block:^(PINCache * _Nonnull cache, NSString * _Nonnull key, id _Nullable object) { NSLog(@"value : %@",cache); }]; //移除都有緩存 [[PINCache sharedCache]removeAllObjects:^(PINCache * _Nonnull cache) { NSLog(@"value : %@",cache); }]; //根據截至日期 清除緩存 [[PINCache sharedCache]trimToDate:[NSDate date] block:^(PINCache * _Nonnull cache) { NSLog(@"value : %@",cache); }];
PINCache使用起來還是非常容易的。
PINCache緩存LRU清理機制
LRU(Least Recently Used)算法大家都比較熟悉,翻譯過來就是“最近最少使用”,LRU緩存就是使用這種原理實現,簡單的說就是緩存一定量的數據,當超過設定的閾值時就把一些過期的數據刪除掉,比如我們緩存10000條數據,當數據小於10000時可以隨意添加,當超過10000時就需要把新的數據添加進來,同時要把過期數據刪除,以確保我們最大緩存10000條,那怎么確定刪除哪條過期數據呢,采用LRU算法實現的話就是將最老的數據刪掉。接下來我們測試一下,
PINCache *pinCache=[PINCache sharedCache]; //模擬內存使用最大開銷限制是1k [pinCache.memoryCache setCostLimit:1*1024]; //模擬磁盤使用最大開銷限制是10k [pinCache.diskCache setByteLimit:1024*10];
模擬500條數據進行存儲
for(int i =0;i<500;i++){ //模擬數據 NSString *value=@"I want to know who is lcj ?"; //模擬一個key NSString *key=[NSString stringWithFormat:@"whoislcj:%d",i]; //寫入數據 [pinCache setObject:value forKey:key]; } NSLog(@"pinCache.memoryCache.totalCost:%lu",(unsigned long)pinCache.memoryCache.totalCost); NSLog(@"pinCache.memoryCache.costLimit:%lu",(unsigned long)pinCache.memoryCache.costLimit); NSLog(@"pinCache.diskCache.byteCount:%lu",(unsigned long)pinCache.diskCache.byteCount); NSLog(@"pinCache.diskCache.byteLimit:%lu",(unsigned long)pinCache.diskCache.byteLimit);
通過上述的運行結果會發現,diskCache起作用了,memoryCache並沒有起作用,我一步步跟進源碼查看跟蹤到memoryCache.m這段代碼
- (void)setObject:(id)object forKey:(NSString *)key { [self setObject:object forKey:key withCost:0]; }
由於每次保存內存存進去的cost開銷都是0,由於導致totalCost累計也是0,與costLimit對比永遠無法進行內存lru處理,聲明一下:我們用的版本是2.3版本,最新的3.0.1-beta.2也有同樣的問題,是PINCache沒有實現內存LRU還是這僅僅是一個bug就不得而知了。
總結:
內部源碼大致看了一遍,大致明白內部實現原理,由於理解不夠深刻,就不對其源碼進行分析了,大致了解一下具體使用方式。