2017年8月4日更新
根據(jù)這個(gè) Session Optimizing I/O for Performance and Battery Life 的描述蚯根,使用 Asset 還有對(duì)啟動(dòng)優(yōu)化的好處谱净。
如果你是被標(biāo)題吸引進(jìn)來的
可以直接跳到最后看結(jié)論仅政,接下來是對(duì) Asset 為什么能加快對(duì)本地圖片加載速度的探討。
為什么探討 Asset 這個(gè)東西
由于項(xiàng)目中在剛啟動(dòng)的瞬間使用 "-[UIImage imageNamed:]" 會(huì)很慢静汤,所以就打算探討 "-[UIImage imageNamed:]" 的實(shí)現(xiàn)琅催,但是過程中發(fā)現(xiàn)了使用 Asset 對(duì)于圖片的加載有極大的提升,就去探討 Asset 了虫给。
使用 Time Profiler 探索實(shí)現(xiàn)
在使用 Time Profiler 調(diào)試 "-[UIImage imageNamed:]" 時(shí)候藤抡,發(fā)現(xiàn)它實(shí)際是調(diào)用 "-[UIImage imageNamed:inBundle:compatibleWithTraitCollection:]" 的,這時(shí)候萌發(fā)了一個(gè)想法是會(huì)不會(huì)根據(jù)指定 Bundle 的范圍會(huì)加快加載圖片的速度(理由是文件夾小了抹估,減少索引次數(shù))缠黍。
實(shí)驗(yàn)后發(fā)現(xiàn),果然如此棋蚌。把圖片分散到指定 bundle 后的速度大大提升了嫁佳。
這時(shí)候,有同事提示我使用 Asset 會(huì)不會(huì)加快谷暮,因?yàn)?WWDC 有提到過蒿往。所以我就去看了這集 Session,發(fā)現(xiàn)它是這樣描述的湿弦。
發(fā)現(xiàn)使用 Asset 后速度也大大提升瓤漏。到此為止就產(chǎn)生了探討 Asset 這個(gè)東西的需求了。
符號(hào)斷點(diǎn)跟蹤步驟
這里就用兩個(gè)圖片來簡(jiǎn)單描述 "-[UIImage imageNamed:]" 發(fā)生了什么颊埃?
使用 Asset 底層是使用一個(gè)叫 _UIAssetManager 的類去存取圖片蔬充,而使用 Folder 則是走 imageIO。而且即使你是用 Folder 也會(huì)先判斷 Asset 中沒有這張圖片才去走 imageIO 班利。
這里就不展開說饥漫,具體可以根據(jù)圖中的函數(shù)名下符號(hào)斷點(diǎn)跟蹤。(建議使用 chisel 來看各個(gè)類的成員變量)
緩存結(jié)構(gòu)和索引的不同
使用 Asset 的緩存結(jié)構(gòu)是: CUIStructuredThemeStore(https://github.com/billinghamj/iPhone6-iOS8-RuntimeHeaders/blob/master/PrivateFrameworks/CoreUI.framework/CUIStructuredThemeStore.h)
// _cache
[
Hash(A.png) -> 緩存
Hash(B.png) -> 緩存
]
使用 Folder 的緩存結(jié)構(gòu)是: CUIMutableStructuredThemeStore(https://github.com/JaviSoto/iOS10-Runtime-Headers/blob/master/PrivateFrameworks/CoreUI.framework/CUIMutableStructuredThemeStore.h)
// nameIdentifierStore
[
A.png -> 1
B.png -> 2
]
// memoryStore
[
Hash_(1) -> 緩存
Hash_(2) -> 緩存
]
因?yàn)槭褂?Folder 去查找緩存罗标,會(huì)先遍歷 nameIdentifierStore 查找是否有緩存庸队,沒有就從 Folder 讀取。在 Time Profiler 會(huì)有那么多的 "isEqualTo" 比較闯割。
而使用 Asset 則是直接根據(jù)圖片名稱來直接 "objectForKey:" 取出緩存彻消。
兩者速度比較是: 前者 O(n)+O(1),后者 O(1)。
(至于為什么是O(n)? 因?yàn)槲矣^察有O(n)這個(gè)行為宙拉,且 "isEqualTo" 的參數(shù)剛好字典 keys 和當(dāng)前需要圖片的名字宾尚。)
而且看出來建立這個(gè)緩存的時(shí)候,使用 Asset 會(huì)快一點(diǎn)(只需要一個(gè)字典就可以)谢澈。
文件 IO 的不同
假設(shè)都是讀取 5 張圖片
使用 Asset 的 IO 過程是:
open Asset -> read Asset -> close Asset
使用 Folder 的過程是:
open 1.png -> read 1.png -> close 1.png
open 2.png -> read 2.png -> close 2.png
open 3.png -> read 3.png -> close 3.png
open 4.png -> read 4.png -> close 4.png
open 5.png -> read 5.png -> close 5.png
可以看出這里 IO 次數(shù)少了(如果數(shù)量大的話煌贴,比較更加明顯御板,Asset 一直是 1 次IO,而 Folder 會(huì)隨讀取文件的多少而遞增)崔步,而且在 IO Usage 可以看出讀取 Asset 的速度極快稳吮。
asset.car 到底是什么
asset.car 是編譯后打包到項(xiàng)目里的文件。從目前的研究來看井濒,asset.car 其實(shí)是將資源打包并建立索引的二進(jìn)制文件,其頭部包含資源在二進(jìn)制對(duì)應(yīng)的位置(類似 seekTable)列林。
小結(jié)
所以使用 Asset 的速度快的原因有以下幾點(diǎn):
- 緩存格式較優(yōu)瑞你,從而建立和查找緩存的速度也會(huì)加快。
- 圖片儲(chǔ)存方式較優(yōu)希痴,查找圖片位置更快者甲,IO 也更快。
從 Folder 到 Asset砌创,你需要做的只是轉(zhuǎn)移圖片資源(Xcode支持將Folder圖片一鍵導(dǎo)入)虏缸,并且不需要改任何代碼就能使圖片加載速度大大加快。