這是筆者學(xué)習(xí) SDWebImage 源碼時(shí)的筆記南用,對它有著很深的怨念呢萨赁。??
功能
SDWebImage 提供的主要功能如下:
- 提供異步圖片(后臺(tái))下載并保證不阻塞主線程鞭呕;
- 支持內(nèi)存和磁盤雙緩存策略文捶,保證同一 url 不會(huì)請求多次吱瘩;
- 支持 WebP/Gif 等格式道伟;
結(jié)構(gòu)
SDWebImageManager
協(xié)調(diào) SDImageCache
和 SDWebImageDownloader
這兩個(gè)模塊的工作,內(nèi)部使用自定義的 SDWebImageCombinedOperation
直接或間接引用對圖片的緩存查詢以及下載的操作使碾,便于取消蜜徽。
原理
對于每一次的圖片下載操作,imageManager 讓 cacheManager 去緩存中查找票摇,由于在內(nèi)存緩存中查找的速度很快拘鞋,所以只在對磁盤緩存查詢時(shí),才異步在 ioQueue 中執(zhí)行查詢?nèi)蝿?wù)矢门,避免阻塞主線程盆色。查詢完成之后,imageDownloader 開始工作祟剔,如果 “所需緩存不存在/緩存過期/ SDWebImageRefreshCached 開啟”隔躲,downloader 就會(huì)產(chǎn)生一個(gè) loaderOperation(繼承于 NSOperation
)放入 downloadQueue,等待啟動(dòng)峡扩。一旦操作啟動(dòng)蹭越, urlConnection 去下載圖片數(shù)據(jù)。這個(gè)操作是在子線程去執(zhí)行的教届,為了能保證接收到 urlConnection 回調(diào)回來的數(shù)據(jù)响鹃,不能讓這個(gè)線程把 -start
內(nèi)的的代碼執(zhí)行完驾霜,就結(jié)束自己的生命,所以需要 runLoop 去驅(qū)動(dòng)線程接收數(shù)據(jù)买置,直到 “數(shù)據(jù)接收完成/發(fā)生錯(cuò)誤/主動(dòng)取消操作” 才停止這個(gè)runLoop粪糙,接收完成了之后發(fā)送通知并執(zhí)行預(yù)設(shè)的 block。
性能
SDWebImage 的源碼中有兩個(gè)地方提到 ‘performance’ 這個(gè)詞:
-
在
shouldDecompressImages
這個(gè) flag 中的注釋忿项,提到了如果開啟了這個(gè) flag 可以提高性能蓉冈,換來的代價(jià)是消耗更多的內(nèi)存。要弄懂shouldDecompressImages
的意義必須先了解圖片從讀取到顯示的過程1. 從磁盤拷貝數(shù)據(jù)到內(nèi)核緩沖區(qū) 2. 從內(nèi)核緩沖區(qū)復(fù)制數(shù)據(jù)到用戶空間 3. 生成 UIImageView轩触,把圖像數(shù)據(jù)賦值給 UIImageView 4. 如果圖像數(shù)據(jù)為未解碼的 PNG/JPG寞酿,解碼為位圖數(shù)據(jù) 5. CATransaction 捕獲到 UIImageView layer 樹的變化 6. 主線程 Runloop 提交 CATransaction,開始進(jìn)行圖像渲染 6.1 如果數(shù)據(jù)沒有字節(jié)對齊脱柱,Core Animation 會(huì)再拷貝一份數(shù)據(jù)伐弹,進(jìn)行字節(jié)對齊 6.2 GPU處理位圖數(shù)據(jù),進(jìn)行渲染
在這個(gè)過程中我們可以看到榨为,如果我們直接將未經(jīng)解碼的圖像數(shù)據(jù)傳遞給 UIImageView 對象惨好,那么該對象就會(huì)在主線程中去解碼圖像數(shù)據(jù),在圖片特別大的情況下耗時(shí)相應(yīng)的也會(huì)特別多随闺,因此 SDWebImage 在解碼圖片這一步上做了優(yōu)化日川,放到了子線程中執(zhí)行,這樣拖動(dòng) tableView 的時(shí)候不至于阻塞主線程矩乐。
- 在
SDWebImageRefreshCached
這個(gè) flag 中的注釋提到龄句,使用NSURLCache
而非SDWebImageCache
會(huì)降低一點(diǎn)性能。因?yàn)閷τ诿總€(gè) HTTP 請求绰精,我們都可以對NSURLCache
(如果使用)設(shè)置相應(yīng)的緩存策略撒璧。通常在響應(yīng)返回之后,客戶端緩存這一份數(shù)據(jù)笨使,通過響應(yīng)頭的 Expires, Cache-Control 等字段決定這份緩存什么時(shí)候過期卿樱。如果緩存過期,HTTP 允許緩存端發(fā)送條件 GET 請求到服務(wù)器硫椰,詢問這份緩存的內(nèi)容是否仍然是“新鮮的”繁调。如果服務(wù)器對原來客戶端緩存過的數(shù)據(jù)進(jìn)行了更改,那我們就有必要從服務(wù)器中再取一份數(shù)據(jù)靶草。綜上蹄胰,如果某個(gè) url 對應(yīng)的資源時(shí)常發(fā)生變動(dòng),那么我們應(yīng)該啟用NSURLCache
奕翔,而圖片通常是靜態(tài)的裕寨,所以啟用SDWebImageCache
可以跳過詢問緩存是否新鮮這個(gè)步驟,提高一點(diǎn)查詢速度。
總結(jié)
有更好的網(wǎng)絡(luò)圖片加載庫嗎宾袜?當(dāng)然捻艳,據(jù)聞 FastImageCache 和 YYWebImage 做的優(yōu)化似乎更多。
然而現(xiàn)在只能等有空的時(shí)候再膜一番了庆猫。