SDWebImage 簡要介紹
SDWebImage 是一款性能卓越、流行度高的網(wǎng)絡(luò)圖片下載框架。對 SDWebImage 框架代碼解讀的文章數(shù)不勝數(shù),這里只胡說八道框架的設(shè)計思想袱箱。
[注]:推薦南峰子的博客 《源碼解析:SDWebImage 實現(xiàn)分析》
《計算機組成與設(shè)計:硬件/軟件接口》 CPU 和 存儲管理講的很好。第三版义矛、第四版均可发笔。
框架設(shè)計思想剖析
SDWebImage 加載圖片的流程
流程參考這里 《那些著名或非著名的 iOS 面試題 - 前編》
1.入口 setImageWithURL:placeholderImage:options: 會先把 placeholderImage 顯示,然后 SDWebImageManager 根據(jù) URL 開始處理圖片凉翻。
2.進入 SDWebImageManager-downloadWithURL:delegate:options:userInfo:了讨,交給 SDImageCache 從緩存查找圖片是否已經(jīng)下載 queryDiskCacheForKey:delegate:userInfo:.
3.先從內(nèi)存圖片緩存查找是否有圖片,如果內(nèi)存中已經(jīng)有圖片緩存制轰,SDImageCacheDelegate 回調(diào) imageCache:didFindImage:forKey:userInfo: 到 SDWebImageManager前计。
4.SDWebImageManagerDelegate 回調(diào) webImageManager:didFinishWithImage: 到 UIImageView+WebCache 等前端展示圖片。
5.如果內(nèi)存緩存中沒有艇挨,生成 NSInvocationOperation 添加到隊列開始從硬盤查找圖片是否已經(jīng)緩存残炮。
6.根據(jù) URLKey 在硬盤緩存目錄下嘗試讀取圖片文件。這一步是在 NSOperation 進行的操作缩滨,所以回主線程進行結(jié)果回調(diào) notifyDelegate:势就。
7.如果上一操作從硬盤讀取到了圖片,將圖片添加到內(nèi)存緩存中(如果空閑內(nèi)存過小脉漏,會先清空內(nèi)存緩存)苞冯。SDImageCacheDelegate 回調(diào) imageCache:didFindImage:forKey:userInfo:。進而回調(diào)展示圖片侧巨。
8.如果從硬盤緩存目錄讀取不到圖片舅锄,說明所有緩存都不存在該圖片,需要下載圖片司忱,回調(diào) imageCache:didNotFindImageForKey:userInfo:皇忿。
9.共享或重新生成一個下載器 SDWebImageDownloader 開始下載圖片畴蹭。
10.圖片下載由 NSURLConnection 來做,實現(xiàn)相關(guān) delegate 來判斷圖片下載中鳍烁、下載完成和下載失敗叨襟。
11.connection:didReceiveData: 中利用 ImageIO 做了按圖片下載進度加載效果。
12.connectionDidFinishLoading: 數(shù)據(jù)下載完成后交給 SDWebImageDecoder 做圖片解碼處理幔荒。
13.圖片解碼處理在一個 NSOperationQueue 完成糊闽,不會拖慢主線程 UI。如果有需要對下載的圖片進行二次處理爹梁,最好也在這里完成右犹,效率會好很多。
14.在主線程 notifyDelegateOnMainThreadWithInfo: 宣告解碼完成姚垃,imageDecoder:didFinishDecodingImage:userInfo: 回調(diào)給 SDWebImageDownloader念链。
15.imageDownloader:didFinishWithImage: 回調(diào)給 SDWebImageManager 告知圖片下載完成。
16.通知所有的 downloadDelegates 下載完成莉炉,回調(diào)給需要的地方展示圖片钓账。
17.將圖片保存到 SDImageCache 中,內(nèi)存緩存和硬盤緩存同時保存絮宁。寫文件到硬盤也在以單獨 NSInvocationOperation 完成梆暮,避免拖慢主線程。
18.SDImageCache 在初始化的時候會注冊一些消息通知绍昂,在內(nèi)存警告或退到后臺的時候清理內(nèi)存圖片緩存啦粹,應(yīng)用結(jié)束的時候清理過期圖片。
總結(jié)
總體設(shè)計思路
- 內(nèi)存中維護一個圖片緩存作為一級緩存窘游,輔存中維護一個圖片緩存作為二級緩存唠椭。同時共享的下載器緩存下載任務(wù),減少下載時的資源消耗忍饰。進行文件查找贪嫂、文件下載、圖片解碼艾蓝、文件讀寫操作等都使用后臺線程完成力崇,減少對程序性能的影響。
設(shè)計經(jīng)驗
- 設(shè)計模式
Observer赢织、Delegate 亮靴、Singleton 的混合使用,使各功能組件的配合更加高效于置。這些簡單的設(shè)計模式也沒有那么不堪茧吊,關(guān)鍵是不亂套、不濫用。 - 性能優(yōu)化
- 功能明確分開
文件查找搓侄、文件下載瞄桨、文件讀寫、圖片解碼等操作都交由對應(yīng)的功能組件完成讶踪,代碼清晰讲婚,結(jié)構(gòu)明確。
- 功能明確分開
- 多線程的使用
處理必要的 UI 數(shù)據(jù)交換俊柔,其他的操作都盡可能的在后臺線程完成,減少對主線程的影響活合。 - 架構(gòu)設(shè)計
- 多級緩存的靈活使用
內(nèi)存中緩存圖片作為一級緩存雏婶,輔存中緩存圖片作為二級緩存;還對可能出現(xiàn)資源損耗的下載操作實現(xiàn)一次緩存白指。
[注] 緩存的使用不是越多多好留晚,請慎重選擇使用。 - 功能組件的設(shè)置
將功能組件分開告嘲,各司其職错维。做到代碼結(jié)構(gòu)清晰,功能有條不紊橄唬。(這里將‘文件下載’赋焕、‘文件IO’的操作稱為功能組件并不合適,只是為了便于理解思路仰楚。)
題外話
- 多線程
什么時候應(yīng)該使用多線程隆判? - 完成線程或者任務(wù)間的同步操作
- 減少多主線程的性能影響,提高反應(yīng)速度僧界。使用后臺線程侨嘀。
多線程中應(yīng)該考慮什么?
添加什么方式的任務(wù)到隊列中捂襟?同步方式還是異步方式咬腕?(同步任務(wù)和異步任務(wù)的區(qū)別是什么?為是否阻塞當前線程葬荷,以同步方式添加任務(wù)到隊列中會阻塞當前線程)
怎么去執(zhí)行線程涨共?串行還是并行?
3. 線程安全的闯狱?
典型的解決線程安全問題的方法:使用線程鎖實現(xiàn)煞赢。對應(yīng)各有優(yōu)缺點。
這里推薦解決線程安全問題幾篇文章:1.Objc 線程安全類的設(shè)計系列文章 2.多線程開發(fā)之線程安全 3.簡書上的 iOS 多線程開發(fā)-線程安全
[注]1. 一個典型的 GCD 死鎖問題
// 1 打印當前線程號
NSLog("之前 - %@", NSThread.currentThread())
// 2 在當前線程中哄孤,向主隊列中添加一個同步任務(wù)
dispatch_sync(dispatch_get_main_queue(), { () -> Void in
NSLog("sync - %@", NSThread.currentThread())
})
// 3 沒有切換線程照筑,在主線程中打印
NSLog("之后 - %@", NSThread.currentThread())
結(jié)果:只能輸出:之前 - XX
步驟分解:
1 在主線程中先獲取主線程隊列,以同步的方式添加一個任務(wù)到主線程隊列中。
2 主線程隊列中的任務(wù)會被取出串行執(zhí)行凝危,同時因為是同步方式波俄,所以會阻塞當前主隊列線程
3 主線程被阻塞,不能被執(zhí)行
[注] 2. 實現(xiàn)線程間數(shù)據(jù)同步或者通信
兩種實現(xiàn)方式:GCD 實現(xiàn) 和 NSOperation 實現(xiàn)蛾默。
- 緩存
強烈推薦閱讀《計算機組成與設(shè)計:硬件與軟件接口》懦铺。 - 架構(gòu)
好的架構(gòu)是一步一步進化出來的,而不是一次就完成所有架構(gòu)的支鸡。但是不可否認冬念,編寫代碼時,應(yīng)該遵循良好的編碼規(guī)范和設(shè)計方法牧挣。
小技巧
- 不同網(wǎng)絡(luò)狀態(tài)下的圖片加載處理
場景:3/4G 和 WiFi 環(huán)境下的高清圖片和普通質(zhì)量圖片的顯示問題
SDWebImage 會默認加載緩存中的高清圖片急前,為了節(jié)省用戶的流量,應(yīng)該對網(wǎng)絡(luò)狀態(tài)進行判斷瀑构,控制加載不同質(zhì)量的圖片裆针。但是這還沒有完,有些用戶默認設(shè)置在 3/4 G 網(wǎng)絡(luò)環(huán)境下加載普通質(zhì)量的圖片(設(shè)置在偏好設(shè)置中)寺晌,該怎么進行省流處理世吨?
例子:《iOS 開發(fā)-你真的會用 SDWebImage?》 - 下載進度展示
場景:正在下載的 GIF 圖片呻征,被點擊瀏覽大圖耘婚,怎么保證小圖和大圖中顯示的下載進度是一致的? - SDWebImage 設(shè)置圖片圓角怕犁,避免離屏渲染
場景:設(shè)置圖片圓角边篮,既要快,又不能有離屏渲染奏甫。
key: 在 ImageView 的分類中戈轿,開啟后臺線程使用 Q2D 進行圖片繪制。
任務(wù)
- 自己實現(xiàn)一個文件下載器
例子:《高效圖片輪播 - 兩個 UIImageView 實現(xiàn)》 - 斷點續(xù)傳
key:使用 HTTP 中的 HEAD 方法阵子,設(shè)置每次下載的文件長度思杯。
其他的方法請評論告知我,謝謝挠进。