iOS面試題:SDWebImage原理

SDWebImage

一個為UIImageView提供一個分類來支持遠(yuǎn)程服務(wù)器圖片加載的庫膨蛮。

功能簡介:

      1敞葛、一個添加了web圖片加載和緩存管理的UIImageView分類
      2惹谐、一個異步圖片下載器
      3、一個異步的內(nèi)存加磁盤綜合存儲圖片并且自動處理過期圖片
      4综液、支持動態(tài)gif圖
      5、支持webP格式的圖片
      6檩奠、后臺圖片解壓處理
      7埠戳、確保同樣的圖片url不會下載多次
      8整胃、確保偽造的圖片url不會重復(fù)嘗試下載
      9屁使、確保主線程不會阻塞

工作流程

1蛮寂、入口 setImageWithURL:placeholderImage:options: 會先把 placeholderImage 顯示,然后 SDWebImageManager 根據(jù) URL 開始處理圖片及老。

2骄恶、進(jìn)入 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 進(jìn)行的操作,所以回主線程進(jìn)行結(jié)果回調(diào) notifyDelegate:蹋肮。

7胶滋、如果上一操作從硬盤讀取到了圖片,將圖片添加到內(nèi)存緩存中(如果空閑內(nèi)存過小皆的,會先清空內(nèi)存緩存)覆履。SDImageCacheDelegate 回調(diào) imageCache:didFindImage:forKey:userInfo:。進(jìn)而回調(diào)展示圖片费薄。

8硝全、如果從硬盤緩存目錄讀取不到圖片,說明所有緩存都不存在該圖片楞抡,需要下載圖片伟众,回調(diào) imageCache:didNotFindImageForKey:userInfo:。

9召廷、共享或重新生成一個下載器 SDWebImageDownloader 開始下載圖片凳厢。

10、圖片下載由 NSURLConnection 來做竞慢,實現(xiàn)相關(guān) delegate 來判斷圖片下載中先紫、下載完成和下載失敗。

11筹煮、connection:didReceiveData: 中利用 ImageIO 做了按圖片下載進(jìn)度加載效果泡孩。connectionDidFinishLoading: 數(shù)據(jù)下載完成后交給 SDWebImageDecoder 做圖片解碼處理。

12寺谤、圖片解碼處理在一個 NSOperationQueue 完成仑鸥,不會拖慢主線程 UI。如果有需要對下載的圖片進(jìn)行二次處理变屁,最好也在這里完成眼俊,效率會好很多。

13粟关、在主線程 notifyDelegateOnMainThreadWithInfo: 宣告解碼完成疮胖,imageDecoder:didFinishDecodingImage:userInfo: 回調(diào)給 SDWebImageDownloader。imageDownloader:didFinishWithImage: 回調(diào)給 SDWebImageManager 告知圖片下載完成。

14澎灸、通知所有的 downloadDelegates 下載完成院塞,回調(diào)給需要的地方展示圖片。將圖片保存到 SDImageCache 中性昭,內(nèi)存緩存和硬盤緩存同時保存拦止。寫文件到硬盤也在以單獨 NSInvocationOperation 完成,避免拖慢主線程糜颠。

15汹族、SDImageCache 在初始化的時候會注冊一些消息通知,在內(nèi)存警告或退到后臺的時候清理內(nèi)存圖片緩存其兴,應(yīng)用結(jié)束的時候清理過期圖片顶瞒。

16、SDWI 也提供了 UIButton+WebCache 和 MKAnnotationView+WebCache元旬,方便使用榴徐。

17、SDWebImagePrefetcher 可以預(yù)先下載圖片匀归,方便后續(xù)使用箕速。

源碼分析

主要用到的對象

一、圖片下載

1朋譬、 SDWebImageDownloader

  • 1.單例,圖片下載器兴垦,負(fù)責(zé)圖片異步下載徙赢,并對圖片加載做了優(yōu)化處理

  • 2.圖片的下載操作放在一個NSOperationQueue并發(fā)操作隊列中,隊列默認(rèn)最大并發(fā)數(shù)是6

  • 3.每個圖片對應(yīng)一些回調(diào)(下載進(jìn)度探越,完成回調(diào)等)狡赐,回調(diào)信息會存在downloader的URLCallbacks(一個字典,key是url地址钦幔,value是圖片下載回調(diào)數(shù)組)中枕屉,URLCallbacks可能被多個線程訪問,所以downloader把下載任務(wù)放在一個barrierQueue中鲤氢,并設(shè)置屏障保證同一時間只有一個線程訪問URLCallbacks搀擂。,在創(chuàng)建回調(diào)URLCallbacks的block中創(chuàng)建了一個NSOperation并添加到NSOperationQueue中卷玉。

  • 4.每個圖片下載都是一個operation類哨颂,創(chuàng)建后添加到一個隊列中,SDWebimage定義了一個協(xié)議 SDWebImageOperation作為圖片下載操作的基礎(chǔ)協(xié)議相种,聲明了一個cancel方法威恼,用于取消操作。

@protocol SDWebImageOperation <NSObject>
-(void)cancel;
@end

  • 5.對于圖片的下載,SDWebImageDownloaderOperation完全依賴于NSURLConnection類箫措,繼承和實現(xiàn)了NSURLConnectionDataDelegate協(xié)議的方法
connection:didReceiveResponse:
connection:didReceiveData:
connectionDidFinishLoading:
connection:didFailWithError:
connection:willCacheResponse:
connectionShouldUseCredentialStorage:
-connection:willSendRequestForAuthenticationChalleng
-connection:didReceiveData:方法腹备,接受數(shù)據(jù),創(chuàng)建一個CGImageSourceRef對象斤蔓,在首次獲取數(shù)據(jù)時(圖片width植酥,height),圖片下載完成之前附迷,使用CGImageSourceRef對象創(chuàng)建一個圖片對象惧互,經(jīng)過縮放、解壓操作生成一個UIImage對象供回調(diào)使用喇伯,同時還有下載進(jìn)度處理喊儡。
注:縮放:SDWebImageCompat中SDScaledImageForKey函數(shù)
 解壓:SDWebImageDecoder文件中decodedImageWithImage

2、SDWebImageDownloaderOption

  • 1.繼承自NSOperation類稻据,沒有簡單實現(xiàn)main方法艾猜,而是采用更加靈活的start方法,以便自己管理下載的狀態(tài)

  • 2.start方法中創(chuàng)建了下載使用的NSURLConnections對象捻悯,開啟了圖片的下載匆赃,并拋出一個下載開始的通知,

  • 3.小結(jié):下載的核心是利用NSURLSession加載數(shù)據(jù)今缚,每個圖片的下載都有一個operation操作來完成算柳,并將這些操作放到一個操作隊列中,這樣可以實現(xiàn)圖片的并發(fā)下載姓言。

3瞬项、SDWebImageDecoder(異步對圖片進(jìn)行解碼)

二、緩存

減少網(wǎng)絡(luò)流量何荚,下載完圖片后存儲到本地囱淋,下載再獲取同一張圖片時,直接從本地獲取餐塘,提升用戶體驗妥衣,能快速從本地獲取呈現(xiàn)給用戶。
SDWebImage提供了對圖片進(jìn)行了緩存戒傻,主要由SDImageCache完成税手。該類負(fù)責(zé)處理內(nèi)存緩存以及一個可選的磁盤緩存,其中磁盤緩存的寫操作是異步的需纳,不會對UI造成影響冈止。

1、內(nèi)存緩存及磁盤緩存

  • 1.內(nèi)存緩存的處理由NSCache對象實現(xiàn)候齿,NSCache類似一個集合的容器熙暴,它存儲key-value對闺属,類似于nsdictionary類,我們通常使用緩存來臨時存儲短時間使用但創(chuàng)建昂貴的對象周霉,重用這些對象可以優(yōu)化新能掂器,同時這些對象對于程序來說不是緊要的,如果內(nèi)存緊張就會自動釋放俱箱。

  • 2.磁盤緩存的處理使用NSFileManager對象實現(xiàn)国瓮,圖片存儲的位置位于cache文件夾,另外SDImageCache還定義了一個串行隊列來異步存儲圖片狞谱。

  • 3.SDImageCache提供了大量方法來緩存乃摹、獲取、移除及清空圖片跟衅。對于圖片的索引孵睬,我們通過一個key來索引,在內(nèi)存中伶跷,我們將其作為NSCache的key值掰读,而在磁盤中,我們用這個key值作為圖片的文件名叭莫,對于一個遠(yuǎn)程下載的圖片其url實作為這個key的最佳選擇蹈集。

2、存儲圖片
先在內(nèi)存中放置一份緩存雇初,如果需要緩存到磁盤拢肆,將磁盤緩存操作作為一個task放到串行隊列中處理,會先檢查圖片格式是jpeg還是png靖诗,將其轉(zhuǎn)換為響應(yīng)的圖片數(shù)據(jù)郭怪,最后吧數(shù)據(jù)寫入磁盤中(文件名是對key值做MD5后的串)

3、查詢圖片
內(nèi)存和磁盤查詢圖片API:

- (UIImage *)imageFromMemoryCacheForKey:(NSString *)key;
- (UIImage *)imageFromDiskCacheForKey:(NSString *)key;

查看本地是否存在key指定的圖片呻畸,使用一下API:

- (NSOperation *)queryDiskCacheForKey:(NSString *)key done:(SDWebImageQueryCompletedBlock)doneBlock;

4、移除圖片
移除圖片API:

- (void)removeImageForKey:(NSString *)key;
- (void)removeImageForKey:(NSString *)key withCompletion:(SDWebImageNoParamsBlock)completion;
- (void)removeImageForKey:(NSString *)key fromDisk:(BOOL)fromDisk;
- (void)removeImageForKey:(NSString *)key fromDisk:(BOOL)fromDisk withCompletion:(SDWebImageNoParamsBlock)completion;

5悼院、清理圖片(磁盤)

清空磁盤圖片可以選擇完全清空和部分清空伤为,完全清空就是吧緩存文件夾刪除。

- (void)clearDisk;
- (void)clearDiskOnCompletion:(SDWebImageNoParamsBlock)completion;

部分清理 會根據(jù)設(shè)置的一些參數(shù)移除部分文件据途,主要有兩個指標(biāo):文件的緩存有效期(maxCacheAge:默認(rèn)是1周)和最大緩存空間大薪视蕖(maxCacheSize:如果所有文件大小大于最大值,會按照文件最后修改時間的逆序颖医,以每次一半的遞歸來移除哪些過早的文件位衩,知道緩存文件總大小小于最大值),具體代碼參考- (void)cleanDiskWithCompletionBlock熔萧;

6糖驴、小結(jié)
SDImageCache處理提供以上API僚祷,還提供了獲取緩存大小,緩存中圖片數(shù)量等API贮缕,
常用的接口和屬性:

(1)-getSize  :獲得硬盤緩存的大小

(2)-getDiskCount : 獲得硬盤緩存的圖片數(shù)量

(3)-clearMemory  : 清理所有內(nèi)存圖片

(4)- removeImageForKey:(NSString *)key  系列的方法 : 從內(nèi)存辙谜、硬盤按要求指定清除圖片

(5)maxMemoryCost  :  保存在存儲器中像素的總和

(6)maxCacheSize  :  最大緩存大小 以字節(jié)為單位。默認(rèn)沒有設(shè)置感昼,也就是為0装哆,而清理磁盤緩存的先決條件為self.maxCacheSize > 0,所以0表示無限制定嗓。

(7)maxCacheAge : 在內(nèi)存緩存保留的最長時間以秒為單位計算蜕琴,默認(rèn)是一周

三、SDWebImageManager

實際使用中并不直接使用SDWebImageDownloader和SDImageCache類對圖片進(jìn)行下載和存儲宵溅,而是使用SDWebImageManager來管理凌简。包括平常使用UIImageView+WebCache等控件的分類,都是使用SDWebImageManager來處理层玲,該對象內(nèi)部定義了一個圖片下載器(SDWebImageDownloader)和圖片緩存(SDImageCache)

@interface SDWebImageManager : NSObject

@property (weak, nonatomic) id <SDWebImageManagerDelegate> delegate;

@property (strong, nonatomic, readonly) SDImageCache *imageCache;
@property (strong, nonatomic, readonly) SDWebImageDownloader *imageDownloader;

...

@end

SDWebImageManager聲明了一個delegate屬性号醉,其實是一個id<SDWebImageManagerDelegate>對象,代理聲明了兩個方法

// 控制當(dāng)圖片在緩存中沒有找到時辛块,應(yīng)該下載哪個圖片
- (BOOL)imageManager:(SDWebImageManager *)imageManager shouldDownloadImageForURL:(NSURL *)imageURL;

// 允許在圖片已經(jīng)被下載完成且被緩存到磁盤或內(nèi)存前立即轉(zhuǎn)換
- (UIImage *)imageManager:(SDWebImageManager *)imageManager transformDownloadedImage:(UIImage *)image withURL:(NSURL *)imageURL;

這兩個方法會在SDWebImageManager的-downloadImageWithURL:options:progress:completed:方法中調(diào)用畔派,而這個方法是SDWebImageManager類的核心所在(具體看源碼)

SDWebImageManager的幾個API:

(1)- (void)cancelAll   : 取消runningOperations中所有的操作,并全部刪除

(2)- (BOOL)isRunning  :檢查是否有操作在運行润绵,這里的操作指的是下載和緩存組成的組合操作

(3) - downloadImageWithURL:options:progress:completed:   核心方法

(4)- (BOOL)diskImageExistsForURL:(NSURL *)url  :指定url的圖片是否進(jìn)行了磁盤緩存

四线椰、視圖擴(kuò)展

在使用SDWebImage的時候,使用最多的是UIImageView+WebCache中的針對UIImageView的擴(kuò)展尘盼,核心方法是sd_setImageWithURL:placeholderImage:options:progress:completed:憨愉, 其使用SDWebImageManager單例對象下載并緩存圖片。

除了擴(kuò)展UIImageView外卿捎,SDWebImage還擴(kuò)展了UIView配紫,UIButton,MKAnnotationView等視圖類午阵,具體可以參考源碼躺孝,除了可以使用擴(kuò)展的方法下載圖片,同時也可以使用SDWebImageManager下載圖片底桂。

UIView+WebCacheOperation分類:
把當(dāng)前view對應(yīng)的圖片操作對象存儲起來(通過運行時設(shè)置屬性)植袍,在基類中完成
存儲的結(jié)構(gòu):一個loadOperationKey屬性,value是一個字典(字典結(jié)構(gòu): key:UIImageViewAnimationImages或者UIImageViewImageLoad籽懦,value是 operation數(shù)組(動態(tài)圖片)或者對象)

UIButton+WebCache分類
會根據(jù)不同的按鈕狀態(tài)于个,下載的圖片根據(jù)不同的狀態(tài)進(jìn)行設(shè)置
imageURLStorageKey:{state:url}

五、技術(shù)點

  • 1.dispatch_barrier_sync函數(shù)暮顺,用于對操作設(shè)置順序厅篓,確保在執(zhí)行完任務(wù)后再確保后續(xù)操作秀存。常用于確保線程安全性操作
  • 2.NSMutableURLRequest:用于創(chuàng)建一個網(wǎng)絡(luò)請求對象,可以根據(jù)需要來配置請求報頭等信息
  • 3.NSOperation及NSOperationQueue:操作隊列是OC中一種告誡的并發(fā)處理方法贷笛,基于GCD實現(xiàn)应又,相對于GCD來說,操作隊列的優(yōu)點是可以取消在任務(wù)處理隊列中的任務(wù)乏苦,另外在管理操作間的依賴關(guān)系方面容易一些株扛,對SDWebImage中我們看到如何使用依賴將下載順序設(shè)置成后進(jìn)先出的順序
  • 4.NSURLSession:用于網(wǎng)絡(luò)請求及相應(yīng)處理
  • 5.開啟后臺任務(wù)
  • 6.NSCache類:一個類似于集合的容器,存儲key-value對汇荐,這一點類似于nsdictionary類洞就,我們通常用使用緩存來臨時存儲短時間使用但創(chuàng)建昂貴的對象。重用這些對象可以優(yōu)化性能掀淘,因為它們的值不需要重新計算旬蟋。另外一方面,這些對象對于程序來說不是緊要的革娄,在內(nèi)存緊張時會被丟棄
  • 7.清理緩存圖片的策略:特別是最大緩存空間大小的設(shè)置倾贰。如果所有緩存文件的總大小超過這一大小,則會按照文件最后修改時間的逆序拦惋,以每次一半的遞歸來移除那些過早的文件匆浙,直到緩存的實際大小小于我們設(shè)置的最大使用空間。
  • 8.圖片解壓操作:這一操作可以查看SDWebImageDecoder.m中+decodedImageWithImage方法的實現(xiàn)厕妖。
  • 9.對GIF圖片的處理
  • 10.對WebP圖片的處理首尼。

收錄:原文地址

以下文章可以做一個學(xué)習(xí)參考:
GCD面試要點
block面試要點
Runtime面試要點
RunLoop面試要點
內(nèi)存管理面試要點
MVC、MVVM面試要點
網(wǎng)絡(luò)性能優(yōu)化面試要點
網(wǎng)絡(luò)編程面試要點
KVC&KVO面試要點
數(shù)據(jù)存儲面試要點
混編技術(shù)面試要點
設(shè)計模式面試要點
UI面試要點

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末言秸,一起剝皮案震驚了整個濱河市软能,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌举畸,老刑警劉巖查排,帶你破解...
    沈念sama閱讀 219,270評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異抄沮,居然都是意外死亡跋核,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評論 3 395
  • 文/潘曉璐 我一進(jìn)店門合是,熙熙樓的掌柜王于貴愁眉苦臉地迎上來了罪,“玉大人锭环,你說我怎么就攤上這事聪全。” “怎么了辅辩?”我有些...
    開封第一講書人閱讀 165,630評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我掸驱,道長加叁,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,906評論 1 295
  • 正文 為了忘掉前任鲤竹,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘悦屏。我一直安慰自己,他們只是感情好键思,可當(dāng)我...
    茶點故事閱讀 67,928評論 6 392
  • 文/花漫 我一把揭開白布础爬。 她就那樣靜靜地躺著,像睡著了一般吼鳞。 火紅的嫁衣襯著肌膚如雪看蚜。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,718評論 1 305
  • 那天赔桌,我揣著相機(jī)與錄音供炎,去河邊找鬼。 笑死疾党,一個胖子當(dāng)著我的面吹牛音诫,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播仿贬,決...
    沈念sama閱讀 40,442評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼纽竣,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了茧泪?” 一聲冷哼從身側(cè)響起蜓氨,我...
    開封第一講書人閱讀 39,345評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎队伟,沒想到半個月后穴吹,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,802評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡嗜侮,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,984評論 3 337
  • 正文 我和宋清朗相戀三年港令,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片锈颗。...
    茶點故事閱讀 40,117評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡顷霹,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出击吱,到底是詐尸還是另有隱情淋淀,我是刑警寧澤,帶...
    沈念sama閱讀 35,810評論 5 346
  • 正文 年R本政府宣布覆醇,位于F島的核電站朵纷,受9級特大地震影響炭臭,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜袍辞,卻給世界環(huán)境...
    茶點故事閱讀 41,462評論 3 331
  • 文/蒙蒙 一鞋仍、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧搅吁,春花似錦威创、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至党瓮,卻和暖如春详炬,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背寞奸。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評論 1 272
  • 我被黑心中介騙來泰國打工呛谜, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人枪萄。 一個月前我還...
    沈念sama閱讀 48,377評論 3 373
  • 正文 我出身青樓隐岛,卻偏偏與公主長得像,于是被迫代替她去往敵國和親瓷翻。 傳聞我的和親對象是個殘疾皇子聚凹,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,060評論 2 355

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