TABAnimated骨架屏快取策略
前言
使用TABAnimated整合骨架屏的開發者,大概都知道其原理是基於原檢視對映生成骨架層,在細節上不滿意的地方可以通過預處理回撥進行非同步調整。
- 本文內容:TABAnimated的快取策略
- 使用TABAnimated的開發者,此文件建議閱讀
TABAnimated快取的是什麼?
快取的是通過對映機制生成的骨架屏單元管理物件TABComponentManager
,
對該物件使用一個plist檔案來解釋。同時,通過計數的方式,逐漸篩選出該使用者經常載入的骨架屏,提高快取命中率。
TABAnimated快取功能有什麼作用?
- 相同版本的程式碼,避開了重複使用對映機制
- 避開了對映採用的遞迴操作,降低了CPU峰值,提高系統性能
- 省去部分場景的檢視預填充成本及其他耗時操作,穩定性得到有效提升
- 預處理回撥需要重新調整CALayer的部分值,有效降低GPU繪製成本
效能評測
- 評測工具:Instruments
- 評測環境:單執行緒
- 評測機型:iPhone 6s
分析
其實,從峰值資料來看,GPU、CPU的數值本身並不高,快取功能也並沒有將他們的峰值降低很多,但是我為什麼還要引入快取策略呢?
答:
- 快取策略更多地是優化了對映機制的耗時。對於CPU效能不高的裝置,在極端的情況下,不能在下一次Vsyc訊號來臨時,將骨架屏的資料計算好並交給GPU處理,此時造成掉幀情況。
當然,掉幀是非常極端的情況。 例如:iPhone6 + 自適應高度 + 不規範的頁面佈局
- 對映機制,是基於當前開發好的檢視。如果我的App打包上線了(除了熱更新),那麼此時骨架屏的對映結果也已經確定了,那麼我還有必要每次都去對映一遍嗎?
答案顯然是不需要的。
TABAnimated採取的是:把對映結果儲存在本地plist檔案(大小約4kb)。唯一注意的是,TABAnimated會讀取你的App版本,當你的App版本發生了變動,會重新對映一次。
正文目錄
- 整合注意
- 快取流程
- 儲存結構
- 執行緒處理
- 特殊場景
一、整合注意
考慮到有些使用者主要關注對後續使用會有什麼影響,所以該點放到第一位。
整合只強調一點!!!也是最重要的一點,反覆強調!!!
TABAnimated新增closeCache
- debug 環境下,預設關閉快取功能(為了方便通過預處理回撥除錯)
- release 環境下,預設開啟快取功能
- 如果你想在 debug 環境下測試快取功能,可以強制置為NO。但是這個時候請注意,預處理回撥再做修改,無效!
- 如果你始終都不想使用快取功能,可以強制置為YES
二、快取流程
下面是流程圖中相關說明:
1. 全域性字典
App在啟動時(圖左側),會預讀取對於該使用者來說載入次數最多的一部分資料到全域性字典。
全域性字典內容:key為plist檔名,value為解釋TABAnimatedManager
物件的plist檔案內容
2. plist檔名
plist檔名用於唯一標識骨架屏管理物件。
-
起初,僅根據className唯一標識。但是有些class會在多個地方,不同
adjustBlock
中出現,即這種方式無法唯一定位某個骨架屏檢視。 -
最好的方式是將原檢視的className+預處理回撥字串化(學過java的應該都用過toString()吧),但是如果回撥處理的東西過多,會浪費大量資源。
-
於是,採取的方案是:在啟動動畫後,獲取當前控制檢視(control view)的
UIViewController
的className, 將其合併。
3. 描述"載入次數最多"、loadCount?
為了描述載入次數最多
,引入了TABAnimatedCacheModel
,TABAnimatedCacheModel
同樣是用一個plist檔案解釋。與TABAnimatedManager
同名
- 一個描述
TABAnimatedCacheModel
的plist檔案,大小約為300bytes - 一個描述
TABAnimatedManager
的plist檔案,大小約為3kb
loadCount欄位作用:
App啟動後,讀取沙盒中所有的TABCacheModel
檔案,根據loadCount
降序排列TABCacheModel
陣列,並載入陣列中前n個TABComponentManager
到記憶體中,儲存方式是全域性字典。(n預設為20)
loadCount更新機制: 啟動動畫後,在下一次runloop執行時,放到序列佇列中。
4. 為什麼要通過TABAnimatedCacheModel
計數,而不是TABAnimatedManager
自計數?
一個用於解釋TABAnimatedManager
的plist檔案大概是4kb,如果僅僅為了更新一個欄位頻繁寫入,很明顯浪費資源。
而TABAnimatedCacheModel
僅需要300bytes。
5. 版本控制
如果開發者在待發布的版本,對某個已經在沙盒中存在的plist檔案,其對應檢視和預處理回撥做了修改,此時需要重新寫入。所以在讀取快取物件前,需要進行版本校對,如果不一致,需要重新使用對映機制。
太長不看?????
三、儲存結構
- 預設生成資料夾
TABAnimated
- 預設在
TABAnimated
資料夾裡生成2個資料夾cacheModel
和cacheManager
-
cacheModel
用於儲存TABAnimatedCacheModel
物件,該物件只有2個欄位,一個用於索引cacheManager
,一個用於描述cacheManager
的載入次數,即loadCount
-
cacheManager
儲存的就是能夠解釋骨架屏的骨架管理物件
儲存路徑:沙盒根目錄/Document/TABAnimated/
四、執行緒處理
- 預設建立一個序列佇列,排程plist檔案寫入、更新任務。
- 預設開闢一個常駐的子執行緒。該執行緒主要負責執行plist檔案寫入、更新任務。通過新增
NSMachPort
埠保證該執行緒的runLoop不會退出。 - 當載入了新的(沙盒中沒有的)骨架物件,該骨架物件會立即寫入全域性字典(即記憶體中),但是並不急著寫入沙盒,會將該任務交由已經建立好的序列佇列排程。
五、特殊場景
如果你的列表資料,需要針對每一個row做特殊處理。
框架內部重寫了getter
方法,檢測到這種情況後,每次都會強制使用對映機制。
當然後續會繼續探索更好的處理方式,目前先這樣處理,不會存在太大的問題~