YYCache源碼學習總結

      YYCache,具體使用就不作介紹了,是由簡書的ibireme大神開發的三方緩存框架。最近項目中遇到了緩存問題,藉此機會讀了一下YYCache的源碼,寫的很是清晰,對做者也是由衷的膜拜。ios

      我也總結了一下YYCache,它的結構兩方面,一是內存緩存,二是磁盤緩存。內存緩存上線程安全使用的pthread_mutex_lock(),經過做者的簡書的描述,看出做者曾經線程安全上使用的OSSpinLock,可是因爲OSSpinLock在新版的ios中已經不能保證線程安全了,因此做者將OSSpinLock替換爲了如今的pthread_mutex_lock()來保證線程安全。算法

      我認爲項目中一旦使用三方框架,就應該去明白框架內部結構與實現原理,一呢是方便後期維護,二呢對本身來講也是種學習。因此在讀YYCache源碼的時候我帶着下面幾個問題去讀。sql

      對於內存緩存上,一是內存緩存是怎麼實現的,二是內存緩存是如何經過內存容量和緩存數量進行清理的,由於在此以前,項目中曾經使用過了大名頂頂的SDWebImage框架作過圖片處理,對於內存釋放上,一旦內存容積達到上限或者存儲數量達到上限就會以通知的方式進行緩存處理。因此既然學習緩存,就要了解它的緩存機制與釋放機制。它的緩存內部用雙向鏈表和 NSDictionary 實現了 LRU 淘汰算法,若是當前節點存在,說明緩存已存在,更新緩存大小/緩存時間/緩存內容,並把當前節點設置爲頭節點。若是當前節點不存在,新建節點,設置緩存大小/緩存時間/緩存內容,插入該節點爲頭節點。緩存釋放上,對外分別提供了時間/數量/容量的接口,實際內部仍然是對鏈表的操做。有一點須要注意的是它會優先移除掉尾節點,固然新插入和最近使用到的也會插入都頭節點。數據庫

      對於磁盤緩存上,一樣仍是上面的兩個問題。磁盤緩存上,它是經過文件加數據庫的方式進行存取。實際文件的操做在YYKVStorage類中。如下注釋爲它的緩存策略:緩存

 *         filename     != null
 *                          則用文件緩存value,並把`key`,`filename`,`extendedData`寫入數據庫
 *         filename     == null
 *                          緩存方式type:YYKVStorageTypeFile 不進行緩存
 *                          緩存方式type:YYKVStorageTypeSQLite||YYKVStorageTypeMixed 數據庫緩存

經過源碼咱們能夠看出,filename生成的條件須要存儲的文件長度大與_inlineThreshold,源碼中_inlineThreshold被設置爲20kb,也就是說文件大小若是大於20kb,那麼就會經過key給此文件生成filename,經過源碼,一旦有filename,value就不會被數據庫存儲,而是寫進文件中,若是沒有filename,數據庫就會對value進行存儲,這也算是做者對數據庫和文件讀寫的充分利用其高性能吧。安全

    if (fileName.length == 0) {
        sqlite3_bind_blob(stmt, 4, value.bytes, (int)value.length, 0);
    } else {
        sqlite3_bind_blob(stmt, 4, NULL, 0, 0);
    }

      還有一個問題須要明白,就是磁盤存儲咱們存在了什麼位置?咱們看下源代碼。YYCache類中的- (instancetype)initWithName:(NSString *)name方法,咱們最後可以找到YYKVStorage中的- (instancetype)initWithPath:(NSString *)path type:(YYKVStorageType)type方法,裏面有幾個路徑咱們須要瞭解下。仍是看下源代碼:框架

//文件存儲路徑  
_dataPath = [path stringByAppendingPathComponent:kDataDirectoryName];
//數據庫打開失敗或者異常垃圾文件回收路徑
_trashPath = [path stringByAppendingPathComponent:kTrashDirectoryName];
//數據庫存儲路徑
_dbPath = [path stringByAppendingPathComponent:kDBFileName];

      關於緩存清理機制,和內存緩存類似,也是經過緩存大小/緩存數量/緩存時間進行清理。仍是看下源碼:性能

/**
 *  刪除全部內存開銷大於size的緩存
 */
- (BOOL)removeItemsLargerThanSize:(int)size;
/**
 *  刪除全部時間比time小的緩存(緩存按時間清理)
 */
- (BOOL)removeItemsEarlierThanTime:(int)time;
/**
 *  減少緩存佔的容量開銷,使總緩存的容量開銷值不大於maxSize(刪除原則:LRU 最久未使用的緩存將先刪除)
 */
- (BOOL)removeItemsToFitSize:(int)maxSize;

/**
 *  減少總緩存數量,使總緩存數量不大於maxCount(刪除原則:LRU 最久未使用的緩存將先刪除)
 */
- (BOOL)removeItemsToFitCount:(int)maxCount;
/**
 *  清空全部緩存
 */
- (BOOL)removeAllItems;
- (void)removeAllItemsWithProgressBlock:(nullable void(^)(int removedCount, int totalCount))progress
                               endBlock:(nullable void(^)(BOOL error))end;

      最後再說一點:經過源碼咱們可以看到,咱們存儲和取出的對象都爲id類型而且實現了NSCoding協議,那麼實際上它是以什麼類型存儲的呢,經過學習

value = [NSKeyedArchiver archivedDataWithRootObject:object]能夠看出是經過歸檔的方式將其歸檔爲NSData類型進行存儲,同理咱們在讀取緩存的時候spa

object = [NSKeyedUnarchiver unarchiveObjectWithData:item.value]將其反序列化拿到id類型的object對象進行使用。

相關文章
相關標籤/搜索