概述
- 倉庫地址:FastImageCache
- 一篇中文簡介,基本講清了 FastImageCache 做了什么:iOS圖片加載速度極限優(yōu)化—FastImageCache解析
要點總結(jié)
- FastImageCache 做了三件事:內(nèi)存映射、解碼圖片、字節(jié)對齊
- 內(nèi)存映射是避免了內(nèi)存復制。傳統(tǒng)的圖片使用方式中讀取一張圖片是先從磁盤讀取到內(nèi)核緩沖區(qū),再由內(nèi)核緩沖區(qū)復制到用戶空間。內(nèi)存映射免除了這一步復制。
- 解碼圖片是避免的解碼過程。提前將圖片解碼成位圖,存儲在磁盤中。
- 字節(jié)對齊是避免 Core Animation 在使用圖像數(shù)據(jù)前拷貝數(shù)據(jù)。Core Animation 使用的數(shù)據(jù)是需要對齊字節(jié)的,傳統(tǒng)過程中提供給 Core Animation 的數(shù)據(jù)未必是對齊的,那么就需要做一次拷貝將字節(jié)對齊。(還有一層含義,下文有詳細說明)
內(nèi)存映射
- 內(nèi)存映射是在 FICImageTableChunk 中完成的,通過 mmap 機制實現(xiàn)的
- 簡單的介紹了 mmap :瞜一眼 mmap
- 詳細的介紹了 mmap :認真分析mmap:是什么 為什么 怎么用
解碼圖片
- 這個地方的實現(xiàn)有點繞,首先創(chuàng)建一個 entry,這個 entry 內(nèi)部的 bytes 已經(jīng)做好內(nèi)存映射了
- 然后用 entry 的 bytes 創(chuàng)建一個 bitmap,這個位圖使用 bytes 中的數(shù)據(jù),反過來如果我們在位圖中繪制內(nèi)容,也會同步到 bytes 對應的內(nèi)存中
- 將圖片繪制到 bitmap 上,這相當于將已經(jīng)解碼的圖片數(shù)據(jù)對應到 bytes 中,然后調(diào)用 entry 的 flush 方法,進行 mmap 機制的同步(即寫入磁盤)
字節(jié)對齊
- 字節(jié)對齊有兩層含義,第一層是字節(jié)塊對齊,第二層是 Core Animation 使用圖像數(shù)據(jù)時的字節(jié)對齊(去除雜質(zhì))
- 字節(jié)塊對齊處理的是 CPU 獲取字節(jié)塊時的優(yōu)化工作,例如 CPU 需要獲取的數(shù)據(jù)在兩個字節(jié)塊中,CPU 就需要加載兩個字節(jié)塊,然后將需要的字節(jié)保留組成所需的字節(jié)塊,對齊后則直接加載所需的字節(jié)塊即可。
- 在 FastImageCache 中是在 FICImageTableChunk 中實現(xiàn)的,據(jù)我不靠譜的觀察,字節(jié)塊對齊不需要額外的工作,圖像數(shù)據(jù)對齊后這里自動就對齊了
- 使用圖像數(shù)據(jù)時的字節(jié)對齊是指,如果圖像數(shù)據(jù)的字節(jié)塊中包含雜質(zhì)(例如一個字節(jié)塊 64 字節(jié),其中有 60 字節(jié)是圖像數(shù)據(jù),最后 4 字節(jié)是其它數(shù)據(jù),對于 Core Animation 來說這 4 字節(jié)就是雜質(zhì)),Core Animation 會復制這個字節(jié)塊的前 60 字節(jié)圖像數(shù)據(jù),最后 4 字節(jié)以 0 填充
- 圖像數(shù)據(jù)的對齊是用 FICByteAlignForCoreAnimation 實現(xiàn)的,有意思的是這里是按照圖像的行數(shù)據(jù)進行對齊,而不是整個圖像的數(shù)據(jù)進行對齊,猜測是繪制時是按行進行取用數(shù)據(jù)的,只有按行對齊才能避免 Core Animation 復制數(shù)據(jù)
使用流程
- 我們來簡單總結(jié)一下 FastImageCache 是如何工作的
- 核心類是 FICImageCache 和 FICImageTable,F(xiàn)ICImageTable 是數(shù)據(jù)存儲的基本框架結(jié)構(gòu),F(xiàn)ICImageCache 是管理類,我們主要與 FICImageCache 打交道
- 我們通過 FICImageCache 的 retrieveImageForEntity 等方法獲取 image,取到后直接給 imageView 等組件賦值即可
- 那么 FICImageCache 是如何獲取原始數(shù)據(jù)的呢?通過其 FICImageCacheDelegate 類型的代理獲取。這也是 FastImageCache 不夠方便的地方,我們需要自己實現(xiàn)代理方法,處理圖片下載等工作。
- FICImageCache 通過代理拿到數(shù)據(jù)后調(diào)用 _processImage 方法來處理圖片,將圖片數(shù)據(jù)放在一個 FICEntityImageDrawingBlock 中傳遞給 imageTable 的 setEntryForEntityUUID 方法
- 在 setEntryForEntityUUID 方法中,imageTable 會生成對應的 chunk 與 entry,使用 entry 的 bytes 創(chuàng)建位圖,使用傳進來的 FICEntityImageDrawingBlock 將圖像繪制在位圖上,那么解壓后的圖像數(shù)據(jù)就對應到了 bytes 中,然后調(diào)用 entry 的 flush 方法將數(shù)據(jù)映射回磁盤文件中
- retrieveImageForEntity 方法內(nèi)部真正獲取圖片的地方調(diào)用了 newImageForEntityUUID ,這里面就比較簡單了,找到對應的 entry,將其 bytes 轉(zhuǎn)換成 UIImage。這時候拿到的 UIImage 就是已經(jīng)解壓過且字節(jié)對齊的數(shù)據(jù)了。
資料
- 一篇精簡扼要的原理介紹,可以作為提綱來了解 FastImageCache:FastImageCache 原理
- 一篇十分詳細的分析:iOS高效圖片 IO 框架是如何煉成的