胡說八道 - 3 SDWebImage 見語

SDWebImage 簡要介紹

SDWebImage 是一款性能卓越、流行度高的網絡圖片下載框架。對 SDWebImage 框架代碼解讀的文章數不勝數,這里只胡說八道框架的設計思想。
[注]:推薦南峰子的博客 《源碼解析:SDWebImage 實現分析》
《計算機組成與設計:硬件/軟件接口》 CPU 和 存儲管理講的很好。第三版、第四版均可。

框架設計思想剖析

SDWebImage 加載圖片的流程

流程參考這里 《那些著名或非著名的 iOS 面試題 - 前編》

1.入口 setImageWithURL:placeholderImage:options: 會先把 placeholderImage 顯示,然后 SDWebImageManager 根據 URL 開始處理圖片。

2.進入 SDWebImageManager-downloadWithURL:delegate:options:userInfo:,交給 SDImageCache 從緩存查找圖片是否已經下載 queryDiskCacheForKey:delegate:userInfo:.

3.先從內存圖片緩存查找是否有圖片,如果內存中已經有圖片緩存,SDImageCacheDelegate 回調 imageCache:didFindImage:forKey:userInfo: 到 SDWebImageManager。

4.SDWebImageManagerDelegate 回調 webImageManager:didFinishWithImage: 到 UIImageView+WebCache 等前端展示圖片。

5.如果內存緩存中沒有,生成 NSInvocationOperation 添加到隊列開始從硬盤查找圖片是否已經緩存。

6.根據 URLKey 在硬盤緩存目錄下嘗試讀取圖片文件。這一步是在 NSOperation 進行的操作,所以回主線程進行結果回調 notifyDelegate:。

7.如果上一操作從硬盤讀取到了圖片,將圖片添加到內存緩存中(如果空閑內存過小,會先清空內存緩存)。SDImageCacheDelegate 回調 imageCache:didFindImage:forKey:userInfo:。進而回調展示圖片。

8.如果從硬盤緩存目錄讀取不到圖片,說明所有緩存都不存在該圖片,需要下載圖片,回調 imageCache:didNotFindImageForKey:userInfo:。

9.共享或重新生成一個下載器 SDWebImageDownloader 開始下載圖片。

10.圖片下載由 NSURLConnection 來做,實現相關 delegate 來判斷圖片下載中、下載完成和下載失敗。

11.connection:didReceiveData: 中利用 ImageIO 做了按圖片下載進度加載效果。

12.connectionDidFinishLoading: 數據下載完成后交給 SDWebImageDecoder 做圖片解碼處理。

13.圖片解碼處理在一個 NSOperationQueue 完成,不會拖慢主線程 UI。如果有需要對下載的圖片進行二次處理,最好也在這里完成,效率會好很多。

14.在主線程 notifyDelegateOnMainThreadWithInfo: 宣告解碼完成,imageDecoder:didFinishDecodingImage:userInfo: 回調給 SDWebImageDownloader。

15.imageDownloader:didFinishWithImage: 回調給 SDWebImageManager 告知圖片下載完成。

16.通知所有的 downloadDelegates 下載完成,回調給需要的地方展示圖片。

17.將圖片保存到 SDImageCache 中,內存緩存和硬盤緩存同時保存。寫文件到硬盤也在以單獨 NSInvocationOperation 完成,避免拖慢主線程。

18.SDImageCache 在初始化的時候會注冊一些消息通知,在內存警告或退到后臺的時候清理內存圖片緩存,應用結束的時候清理過期圖片。

總結

總體設計思路

  1. 內存中維護一個圖片緩存作為一級緩存,輔存中維護一個圖片緩存作為二級緩存。同時共享的下載器緩存下載任務,減少下載時的資源消耗。進行文件查找、文件下載、圖片解碼、文件讀寫操作等都使用后臺線程完成,減少對程序性能的影響。

設計經驗

  1. 設計模式
    Observer、Delegate 、Singleton 的混合使用,使各功能組件的配合更加高效。這些簡單的設計模式也沒有那么不堪,關鍵是不亂套、不濫用。
  2. 性能優化
    1. 功能明確分開
      文件查找、文件下載、文件讀寫、圖片解碼等操作都交由對應的功能組件完成,代碼清晰,結構明確。
  3. 多線程的使用
    處理必要的 UI 數據交換,其他的操作都盡可能的在后臺線程完成,減少對主線程的影響。
  4. 架構設計
  5. 多級緩存的靈活使用
    內存中緩存圖片作為一級緩存,輔存中緩存圖片作為二級緩存;還對可能出現資源損耗的下載操作實現一次緩存。
    [注] 緩存的使用不是越多多好,請慎重選擇使用。
  6. 功能組件的設置
    將功能組件分開,各司其職。做到代碼結構清晰,功能有條不紊。(這里將‘文件下載’、‘文件IO’的操作稱為功能組件并不合適,只是為了便于理解思路。)

題外話

  1. 多線程
    什么時候應該使用多線程?
  2. 完成線程或者任務間的同步操作
  3. 減少多主線程的性能影響,提高反應速度。使用后臺線程。

多線程中應該考慮什么?

  1. 添加什么方式的任務到隊列中?同步方式還是異步方式?(同步任務和異步任務的區別是什么?為是否阻塞當前線程,以同步方式添加任務到隊列中會阻塞當前線程)

  2. 怎么去執行線程?串行還是并行?

3. 線程安全的?
典型的解決線程安全問題的方法:使用線程鎖實現。對應各有優缺點。
這里推薦解決線程安全問題幾篇文章:1.Objc 線程安全類的設計系列文章 2.多線程開發之線程安全 3.簡書上的 iOS 多線程開發-線程安全

[注]1. 一個典型的 GCD 死鎖問題
// 1 打印當前線程號
NSLog("之前 - %@", NSThread.currentThread())
// 2 在當前線程中,向主隊列中添加一個同步任務
dispatch_sync(dispatch_get_main_queue(), { () -> Void in
NSLog("sync - %@", NSThread.currentThread())
})
// 3 沒有切換線程,在主線程中打印
NSLog("之后 - %@", NSThread.currentThread())
結果:只能輸出:之前 - XX
步驟分解:
1 在主線程中先獲取主線程隊列,以同步的方式添加一個任務到主線程隊列中。
2 主線程隊列中的任務會被取出串行執行,同時因為是同步方式,所以會阻塞當前主隊列線程
3 主線程被阻塞,不能被執行
[注] 2. 實現線程間數據同步或者通信
兩種實現方式:GCD 實現 和 NSOperation 實現。

  1. 緩存
    強烈推薦閱讀《計算機組成與設計:硬件與軟件接口》
  2. 架構
    好的架構是一步一步進化出來的,而不是一次就完成所有架構的。但是不可否認,編寫代碼時,應該遵循良好的編碼規范和設計方法

小技巧

  1. 不同網絡狀態下的圖片加載處理
    場景:3/4G 和 WiFi 環境下的高清圖片和普通質量圖片的顯示問題
    SDWebImage 會默認加載緩存中的高清圖片,為了節省用戶的流量,應該對網絡狀態進行判斷,控制加載不同質量的圖片。但是這還沒有完,有些用戶默認設置在 3/4 G 網絡環境下加載普通質量的圖片(設置在偏好設置中),該怎么進行省流處理?
    例子:《iOS 開發-你真的會用 SDWebImage?》
  2. 下載進度展示
    場景:正在下載的 GIF 圖片,被點擊瀏覽大圖,怎么保證小圖和大圖中顯示的下載進度是一致的?
  3. SDWebImage 設置圖片圓角,避免離屏渲染
    場景:設置圖片圓角,既要快,又不能有離屏渲染。
    key: 在 ImageView 的分類中,開啟后臺線程使用 Q2D 進行圖片繪制。

任務

  1. 自己實現一個文件下載器
    例子:《高效圖片輪播 - 兩個 UIImageView 實現》
  2. 斷點續傳
    key:使用 HTTP 中的 HEAD 方法,設置每次下載的文件長度。
    其他的方法請評論告知我,謝謝。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,048評論 6 542
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,414評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,169評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,722評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,465評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,823評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,813評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,000評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,554評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,295評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,513評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,035評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,722評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,125評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,430評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,237評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,482評論 2 379

推薦閱讀更多精彩內容