原始碼解析 — YYCache

發表於2016-12-31
11307963-367208362d4f840d
封面.jpg

前言:準備看下YY系列中的YYWebImage框架,發現該框架是使用YYCache來做快取的。那就從快取開始吧.
先奉上YYCache框架的地址以及作者的設計思路
學習YYCache框架你可以get到:
1.優雅的程式碼風格
2.優秀的介面設計
3.YYCache的層次結構
4.YYMemoryCache類的層次結構和快取機制
5.YYDiskCache類的層次結構和快取機制

YYCache

12307963-a3d92756bf3d36bc
YYCache結構.png

YYCache最為食物鏈的最頂端的男人,並沒有什麼好說的,所以我們就從YYMemoryCacheYYDiskCache開始吧。

YYMemoryCache

YYMemoryCache記憶體儲存是的原理是利用CFDictionary物件的 key-value開闢記憶體儲存機制和雙向連結串列原理來實現LRU演算法。這裡是官方文件對CFDictionary的解釋:

13307963-9600e9680440fdfd
YYMemoryCache類結構圖.png

YYMemoryCache初始化的時候會建立空的私有物件YYLinkedMap連結串列,接下來所有的操作其實就是對這個連結串列的操作。當然,YYMemoryCache提供了一個定時器介面給你,你可以通過設定autoTrimInterval屬性去完成每隔一定時間去檢查countLimitcostLimit是否達到了最大限制,並做相應的操作。

YYMemoryCache以block的形式給你提供了下面介面:

  • didReceiveMemoryWarningBlock(當app接受到記憶體警告)
  • didEnterBackgroundBlock (當app進入到後臺)

當然,你也可以通過設定相應的shouldRemoveAllObjectsOnMemoryWarningshouldRemoveAllObjectsWhenEnteringBackground值來移除YYMemoryCache持有的連結串列。

下面我們來看看YYMemoryCache類的增,刪,查等操作。在這之前我們先看看YYLinkedMap這個類。

1.YYLinkedMap內部結構

YYLinkedMap作為雙向連結串列,主要的工作是為YYMemoryCache類提供對YYLinkedMapNode節點的操作。下圖綠色部分代表節點:

14307963-b7d65e7786919a4f
雙向連結串列結構.png

下圖是連結串列節點的結構圖:

15307963-8b863f1d8ad804e7
連結串列節點.png

現在我們先來看如何去構造一個連結串列新增節點:

16307963-2e0a14f8e758e50e
setObject.png

你可以點選這裡自己去操作雙向連結串列

17307963-1e6f08083f16c341
addNode.gif

連結串列移除節點的操作:

18307963-30e04c1e0a8c53a1
removeNode.gif

YYMemoryCache類還為我們提供了下列介面方便我們呼叫:

總結:YYMemoryCache是利用key-value機制記憶體快取類,所有的方法都是執行緒安全的。如果你熟悉NSCache類,你會發現兩者的介面很是相似。
當然YYMemoryCache有著自己的特點:
1.YYMemoryCache採用LRU(least-recently-used)演算法來移除節點。
2.YYMemoryCache可以用countLimitcostLimitageLimit屬性做相應的控制。
3.YYMemoryCache類可以設定相應的屬性來控制退到後臺或者接受到記憶體警告的時候移除連結串列。

YYKVStorage

YYKVStorage是一個基於sql資料庫和檔案寫入的快取類,注意它並不是執行緒安全。你可以自己定義YYKVStorageType來確定是那種寫入方式:

1.寫入和更新

我們看看Demo中直接用YYKVStorage儲存NSNumber和NSData YYKVStorageTypeFileYYKVStorageTypeSQLite型別所用的時間:

19307963-f7193b38dd0e8df6
7.png

你可以發現在儲存小型資料NSNumberYYKVStorageTypeFile型別是YYKVStorageTypeSQLite大約4倍多,而在大型資料的時候兩者的表現是相反的。顯然選擇合適的儲存方式是很有必要的。
這裡需要提醒的事:

  • DemoYYKVStorageTypeFile型別其實不僅寫入了本地檔案也同時寫入了資料庫,只不過資料庫裡面存的是除了value值以外的key, filename, size, inline_data(NULL), modification_time , last_access_time, extended_data欄位。

插入或者是更新資料庫

2.讀取

我們嘗試的去快取裡面拿取資料,我們發現當為YYKVStorage物件type不同,存取的方式不同所以讀取的方式也不同:
1.因為在插入的時候我們就說了,當為YYKVStorageTypeFile型別的時候資料是存在本地檔案的其他存在資料庫。所以YYKVStorage物件先根據key從資料庫拿到資料然後包裝成YYKVStorageItem物件,然後再根據filename讀取本地檔案資料賦給YYKVStorageItem物件的value屬性。
2.當為YYKVStorageTypeSQLite型別就是直接從資料庫把所有資料都讀出來賦給YYKVStorageItem物件。

3.刪除

YYKVStorage的type當為YYKVStorageTypeFile型別是根據key將本地和資料庫都刪掉,而YYKVStorageTypeSQLite是根據key刪除掉資料庫就好了。

我們這裡分別列取了增刪改查的單個key的操作,你還可以去批量的去操作key的陣列。但是其實都大同小異的流程,就不一一累述了。上個圖吧:

20307963-a9967cf4a949dfcb
這個類也就看的差不多了,但是要注意的事,YYCache作者並不希望我們直接使用這個類,而是使用更高層的YYDiskCache類。那我們就繼續往下面看吧。

YYDiskCache

YYDiskCache類有兩種初始化方式:

YYDiskCache類持有一個YYKVStorage物件,但是你不能手動的去控制YYKVStorage物件的YYKVStorageTypeYYDiskCache類初始化提供一個threshold的引數,預設的為20KB。然後根據這個值得大小來確定YYKVStorageType的型別。

因為YYDiskCache類的操作其實就是去操作持有的YYKVStorage物件,所以下面的部分會比較建簡略。

寫入和更新

在呼叫YYKVStorage物件的儲存操作前主要做了下面幾項操作:
1.key和object的判空容錯機制
2.利用runtime機制去取extendedData資料
3.根據是否定義了_customArchiveBlock來判斷選擇序列化object還是block回撥得到value
4.value的判空容錯機制
5.根據YYKVStorage的type判斷以及_inlineThreshold和value值得長度來判斷是否選擇以檔案的形式儲存value值。上面我們說過當value比較大的時候檔案儲存速度比較快速。
6.如果_customFileNameBlock為空,則根據key通過md5加密得到轉化後的filename.不然直接拿到_customFileNameBlock關聯的filename。生成以後操作檔案的路徑
做完上面的操作則直接呼叫YYKVStorage儲存方法,下面是實現程式碼:

讀取

讀取操作一般都是和寫入操作相輔相成的,我們來看看在呼叫YYKVStorage物件的讀取操作後做了哪些操作:
1.item.value的判空容錯機制
2.根據_customUnarchiveBlock值來判斷是直接將item.value block回撥還是反序列化成object
3.根據object && item.extendedData 來決定是否runtime新增extended_data_key屬性

刪除

刪除操作就是直接呼叫的YYKVStorage物件來操作了。

當然,YYDiskCacheYYMemoryCache一樣也給你提供了一些類似limit的介面供你操作。

YYKVStorage不一樣的是,作為更高層的YYDiskCache是一個執行緒安全的類。你應該使用YYDiskCache而不是YYKVStorage

最後再帶一筆食物端最頂端的男人YYCache,當他寫入的時候會同時呼叫YYDiskCache磁碟操作和YYMemoryCache記憶體操作。讀取的時候先從記憶體讀取,因為在記憶體的讀取速度比磁碟快很多,如果沒有讀取到資料才會去磁碟讀取。

讀後感只有四個字:

如沐春風

相關文章