SDWebImage 源碼閱讀筆記

前不久做了一個生成快照的需求指孤,其中用到 SDWebImage 來下載圖片奏窑,在使用該框架的過程中也遇到了一些問題,索性正好就把 SDWebImage (v3.7.3) 源碼細(xì)讀了一下朱躺,學(xué)習(xí)一下其中的設(shè)計(jì)思想和技術(shù)點(diǎn)雇锡,為了梳理思路,順便寫下了這篇文章贬蛙。
目錄
簡介

設(shè)計(jì)目的

特性

SDWebImage 與其他框架的對比

常見問題

用法

SDWebImage 4.0 遷移指南

實(shí)現(xiàn)原理

架構(gòu)圖

流程圖

目錄結(jié)構(gòu)

核心邏輯

實(shí)現(xiàn)細(xì)節(jié)

  1. 圖片下載

1.1 SDWebImageDownloader

1.2 SDWebImageDownloader

  1. 圖片緩存——SDImageCache

  2. 圖片加載管理器——SDWebImageManager

  3. 設(shè)置 UIImageView 的圖片——UIImageView+WebCache

知識點(diǎn)

收獲與疑問

啟發(fā)與實(shí)踐

延伸閱讀

一雨女、簡介
1. 設(shè)計(jì)目的
SDWebImage 提供了 UIImageView、UIButton 阳准、MKAnnotationView 的圖片下載分類氛堕,只要一行代碼就可以實(shí)現(xiàn)圖片異步下載和緩存功能。這樣開發(fā)者就無須花太多精力在圖片下載細(xì)節(jié)上野蝇,專心處理業(yè)務(wù)邏輯讼稚。
2. 特性
提供 UIImageView, UIButton, MKAnnotationView 的分類,用來顯示網(wǎng)絡(luò)圖片绕沈,以及緩存管理

異步下載圖片

異步緩存(內(nèi)存+磁盤)锐想,并且自動管理緩存有效性

后臺圖片解壓縮

同一個 URL 不會重復(fù)下載

自動識別無效 URL,不會反復(fù)重試

不阻塞主線程

高性能

使用 GCD 和 ARC

支持多種圖片格式(包括 WebP 格式)

支持動圖(GIF)

4.0 之前的動圖效果并不是太好

4.0 以后基于 FLAnimatedImage加載動圖

注:本文選讀的代碼是 3.7.3 版本的乍狐,所以動圖加載還不支持 FLAnimatedImage赠摇。
3. SDWebImage 與其他框架的對比
利益相關(guān):以下兩篇文章都是 SDWebImage 的維護(hù)者所寫,具有一定的主觀性,僅供參考藕帜。
How is SDWebImage better than X?

iOS image caching. Libraries benchmark (SDWebImage vs FastImageCache)

4. 常見問題
問題 1:使用 UITableViewCell 中的 imageView 加載不同尺寸的網(wǎng)絡(luò)圖片時會出現(xiàn)尺寸縮放問題

解決方案:自定義 UITableViewCell烫罩,重寫 -layoutSubviews 方法,調(diào)整位置尺寸洽故;或者直接棄用 UITableViewCell 的 imageView贝攒,自己添加一個 imageView 作為子控件。
問題 2:圖片刷新問題:SDWebImage 在進(jìn)行緩存時忽略了所有服務(wù)器返回的 caching control 設(shè)置收津,并且在緩存時沒有做時間限制饿这,這也就意味著圖片 URL 必須是靜態(tài)的了,要求服務(wù)器上一個 URL 對應(yīng)的圖片內(nèi)容不允許更新撞秋。但是如果存儲圖片的服務(wù)器不由自己控制长捧,也就是說 圖片內(nèi)容更新了,URL 卻沒有更新吻贿,這種情況怎么辦串结?

解決方案:在調(diào)用 sd_setImageWithURL: placeholderImage: options:方法時設(shè)置 options 參數(shù)為 SDWebImageRefreshCached,這樣雖然會降低性能舅列,但是下載圖片時會照顧到服務(wù)器返回的 caching control肌割。
問題 3:在加載圖片時,如何添加默認(rèn)的 progress indicator 帐要?

解決方案:在調(diào)用 -sd_setImageWithURL:方法之前把敞,先調(diào)用下面的方法:
[imageView sd_setShowActivityIndicatorView:YES];
[imageView sd_setIndicatorStyle:UIActivityIndicatorViewStyleGray];
5. 用法
5.1 UITableView 中使用 UIImageView+WebCache
UITabelViewCell 中的 UIImageView 控件直接調(diào)用 sd_setImageWithURL: placeholderImage:方法即可
5.2 使用回調(diào) blocks
在 block 中得到圖片下載進(jìn)度和圖片加載完成(下載完成或者讀取緩存)的回調(diào),如果你在圖片加載完成前取消了請求操作榨惠,就不會收到成功或失敗的回調(diào)

[cell.imageView sd_setImageWithURL:[NSURL URLWithString:@
"[http://www.domain.com/path/to/image.jpg](http://www.domain.com/path/to/image.jpg)"
]

                      
placeholderImage:[UIImage imageNamed:@
"placeholder.png"
]

                             
completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {

                                    
... completion code here ...

                                 
}];

5.3 SDWebImageManager 的使用

UIImageView(WebCache) 分類的核心在于 SDWebImageManager 的下載和緩存處理奋早,SDWebImageManager將圖片下載和圖片緩存組合起來了。SDWebImageManager也可以單獨(dú)使用赠橙。

SDWebImageManager *manager = [SDWebImageManager sharedManager];
    [manager loadImageWithURL:imageURL
                      options:0
                     progress:^(NSInteger receivedSize, NSInteger expectedSize) {
                            // progression tracking code
                     }
                     completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
                        if (image) {
                            // do something with image
                        }
                     }];

5.4 單獨(dú)使用 SDWebImageDownloader 異步下載圖片
我們還可以單獨(dú)使用 SDWebImageDownloader 來下載圖片耽装,但是圖片內(nèi)容不會緩存。

 SDWebImageDownloader *downloader = [SDWebImageDownloader sharedDownloader];
    [downloader downloadImageWithURL:imageURL
                             options:0
                            progress:^(NSInteger receivedSize, NSInteger expectedSize) {
                                // progression tracking code
                            }
                           completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) {
                                if (image && finished) {
                                    // do something with image
                                }
                            }];

5.5 單獨(dú)使用 SDImageCache 異步緩存圖片

SDImageCache 支持內(nèi)存緩存和異步的磁盤緩存(可選)期揪,如果你想單獨(dú)使用 SDImageCache 來緩存數(shù)據(jù)的話掉奄,可以使用單例,也可以創(chuàng)建一個有獨(dú)立命名空間的 SDImageCache 實(shí)例凤薛。

添加緩存的方法:
[[SDImageCache sharedImageCache] storeImage:myImage forKey:myCacheKey];
默認(rèn)情況下姓建,圖片數(shù)據(jù)會同時緩存到內(nèi)存和磁盤中,如果你想只要內(nèi)存緩存的話枉侧,可以使用下面的方法:
[[SDImageCache sharedImageCache] storeImage:myImage forKey:myCacheKey toDisk:NO];
讀取緩存時可以使用 queryDiskCacheForKey:done: 方法引瀑,圖片緩存的 key 是唯一的,通常就是圖片的 absolute URL榨馁。

SDImageCache *imageCache = [[SDImageCache alloc] initWithNamespace:@"myNamespace"];
    [imageCache queryDiskCacheForKey:myCacheKey done:^(UIImage *image) {
        // image is not nil if image was found
    }];

5.6 自定義緩存 key

有時候,一張圖片的 URL 中的一部分可能是動態(tài)變化的(比如獲取權(quán)限上的限制)帜矾,所以我們只需要把 URL 中不變的部分作為緩存用的 key翼虫。

   SDWebImageManager.sharedManager.cacheKeyFilter = ^(NSURL *url) {
            url = [[NSURL alloc] initWithScheme:url.scheme host:url.host path:url.path];
            return [url absoluteString];
        };

6. SDWebImage 4.0 遷移指南
按照版本號慣例(Semantic Versioning)屑柔,從版本號可以看出 SDWebImage 4.0 是一個大版本,在結(jié)構(gòu)上和 API 方面都有所改動珍剑。
除了 iOS 和 tvOS 之外掸宛,SDWebImage 4.0 還支持更多的平臺——watchOS 和 Max OS X。
借助 FLAnimatedImage 在動圖支持上做了改進(jìn)招拙,尤其是 GIF唧瘾。

1. 目錄結(jié)構(gòu)
Downloader

SDWebImageDownloader

SDWebImageDownloaderOperation

Cache

SDImageCache

Utils

SDWebImageManager

SDWebImageDecoder

SDWebImagePrefetcher

Categories

UIView+WebCacheOperation

UIImageView+WebCache

UIImageView+HighlightedWebCache

UIButton+WebCache

MKAnnotationView+WebCache

NSData+ImageContentType

UIImage+GIF

UIImage+MultiFormat

UIImage+WebP

Other

SDWebImageOperation(協(xié)議)

SDWebImageCompat(宏定義、常量别凤、通用函數(shù))

1494499913406062.png
  1. 核心邏輯

下載 Source code(3.7.3)饰序,運(yùn)行 pod install,然后打開 SDWebImage.xcworkspace规哪,先 run 起來感受一下求豫。

在了解細(xì)節(jié)之前我們先大概瀏覽一遍主流程,也就是最核心的邏輯诉稍。

我們從 MasterViewController 中的 [cell.imageView sd_setImageWithURL:url placeholderImage:placeholderImage]; 開始看起蝠嘉。

經(jīng)過層層調(diào)用,直到 UIImageView+WebCache 中最核心的方法 sd_setImageWithURL: placeholderImage: options: progress: completed:杯巨。該方法中蚤告,主要做了以下幾件事:

取消當(dāng)前正在進(jìn)行的加載任務(wù) operation
設(shè)置 placeholder
如果 URL 不為 nil,就通過 SDWebImageManager 單例開啟圖片加載任務(wù) operation服爷,SDWebImageManager 的圖片加載方法中會返回一個 SDWebImageCombinedOperation 對象杜恰,這個對象包含一個 cacheOperation 和一個 cancelBlock。
SDWebImageManager 的圖片加載方法 downloadImageWithURL:options:progress:completed: 中會先拿圖片緩存的 key (這個 key 默認(rèn)是圖片 URL)去 SDImageCache 單例中讀取內(nèi)存緩存层扶,如果有箫章,就返回給 SDWebImageManager;如果內(nèi)存緩存沒有镜会,就開啟異步線程檬寂,拿經(jīng)過 MD5 處理的 key 去讀取磁盤緩存,如果找到磁盤緩存了戳表,就同步到內(nèi)存緩存中去桶至,然后再返回給 SDWebImageManager。

如果內(nèi)存緩存和磁盤緩存中都沒有匾旭,SDWebImageManager 就會調(diào)用 SDWebImageDownloader 單例的 -downloadImageWithURL: options: progress: completed: 方法去下載镣屹,該會先將傳入的 progressBlock 和 completedBlock 保存起來,并在第一次下載該 URL 的圖片時价涝,創(chuàng)建一個 NSMutableURLRequest 對象和一個 SDWebImageDownloaderOperation 對象女蜈,并將該 SDWebImageDownloaderOperation 對象添加到 SDWebImageDownloader 的downloadQueue 來啟動異步下載任務(wù)。

SDWebImageDownloaderOperation 中包裝了一個 NSURLConnection 的網(wǎng)絡(luò)請求,并通過 runloop 來保持 NSURLConnection 在 start 后伪窖、收到響應(yīng)前不被干掉逸寓,下載圖片時,監(jiān)聽 NSURLConnection 回調(diào)的 -connection:didReceiveData: 方法中會負(fù)責(zé) progress 相關(guān)的處理和回調(diào)覆山,- connectionDidFinishLoading: 方法中會負(fù)責(zé)將 data 轉(zhuǎn)為 image竹伸,以及圖片解碼操作,并最終回調(diào) completedBlock簇宽。

SDWebImageDownloaderOperation 中的圖片下載請求完成后勋篓,會回調(diào)給 SDWebImageDownloader,然后 SDWebImageDownloader 再回調(diào)給 SDWebImageManager魏割,SDWebImageManager 中再將圖片分別緩存到內(nèi)存和磁盤上(可選)譬嚣,并回調(diào)給 UIImageView,UIImageView 中再回到主線程設(shè)置 image 屬性见妒。至此孤荣,圖片的下載和緩存操作就圓滿結(jié)束了。

當(dāng)然须揣,SDWebImage 中還有很多細(xì)節(jié)可以深挖盐股,包括一些巧妙設(shè)計(jì)和知識點(diǎn),接下來再看看SDWebImage 中的實(shí)現(xiàn)細(xì)節(jié)耻卡。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末疯汁,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子卵酪,更是在濱河造成了極大的恐慌幌蚊,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件溃卡,死亡現(xiàn)場離奇詭異溢豆,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)瘸羡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進(jìn)店門漩仙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人犹赖,你說我怎么就攤上這事队他。” “怎么了峻村?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵麸折,是天一觀的道長。 經(jīng)常有香客問我粘昨,道長垢啼,這世上最難降的妖魔是什么窜锯? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮膊夹,結(jié)果婚禮上衬浑,老公的妹妹穿的比我還像新娘捌浩。我一直安慰自己放刨,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布尸饺。 她就那樣靜靜地躺著进统,像睡著了一般。 火紅的嫁衣襯著肌膚如雪浪听。 梳的紋絲不亂的頭發(fā)上螟碎,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天,我揣著相機(jī)與錄音迹栓,去河邊找鬼掉分。 笑死,一個胖子當(dāng)著我的面吹牛克伊,可吹牛的內(nèi)容都是我干的酥郭。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼愿吹,長吁一口氣:“原來是場噩夢啊……” “哼不从!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起犁跪,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤椿息,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后坷衍,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體寝优,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年枫耳,在試婚紗的時候發(fā)現(xiàn)自己被綠了乏矾。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡嘉涌,死狀恐怖妻熊,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情仑最,我是刑警寧澤扔役,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響臂外,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜益咬,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一侈玄、第九天 我趴在偏房一處隱蔽的房頂上張望婉刀。 院中可真熱鬧,春花似錦序仙、人聲如沸突颊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽律秃。三九已至,卻和暖如春治唤,著一層夾襖步出監(jiān)牢的瞬間棒动,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工宾添, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留船惨,地道東北人。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓缕陕,卻偏偏與公主長得像粱锐,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子榄檬,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容