一行行看SDWebImage原始碼(一)

發表於2016-07-04
276769-f7b02c377c44f9ea
地表最強

SDWebImage是iOS開發者經常使用的一個開源框架,這個框架的主要作用是:
一個非同步下載圖片並且支援快取的UIImageView分類.

UIImageView+WebCache

我們最常用的方法就是這個:

現在開始我們一步步地看這個方法的內部實現:

這裡會呼叫下面這個方法:

我們看UIImageView+WebCache.h檔案,我們可以發現為開發者提供了很多類似於- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder的方法.
這些方法最終都會呼叫- sd_setImageWithURL: placeholderImage: options: progress: completed:,這個方法可以算是核心方法,下面我們看一下- sd_setImageWithURL: placeholderImage: options: progress: completed:
方法的實現:

首先執行

接下來我們來看看[self sd_cancelCurrentImageLoad]內部是怎麼執行的:


UIView+WebCacheOperation

下面我們先來看看UIView+WebCacheOperation裡面都寫了些什麼:
UIView+WebCacheOperation這個分類提供了三個方法,用於操作繫結關係

為了方便管理和找到檢視正在進行的一些操作,WebCacheOperation將每一個檢視的例項和它正在進行的操作(下載和快取的組合操作)繫結起來,實現操作和檢視一一對應關係,以便可以隨時拿到檢視正在進行的操作,控制其取消等,如何進行繫結我們在下面分析:
UIView+WebCacheOperation.m檔案內
- (NSMutableDictionary *)operationDictionary用到了中定義的兩個函式:

  • objc_setAssociatedObject
  • objc_getAssociatedObject

    NSObject+AssociatedObject.h

    NSObject+AssociatedObject.m

objc_setAssociatedObject作用是對已存在的類在擴充套件中新增自定義的屬性 ,通常推薦的做法是新增屬性的key最好是static char型別的,通常來說該屬性的key應該是常量唯一的.
objc_getAssociatedObject根據key獲得與物件繫結的屬性.

接下來我們繼續探索
- sd_setImageWithURL: placeholderImage: options: progress: completed:


SDWebImageManager

-sd_setImageWithURL:forState:placeholderImage:options:completed:中,下載圖片方法是位於SDWebImageManager類中- downloadImageWithURL: options:progress:completed:函式
我們看下文件是對SDWebImageManager怎麼描述的

我們繼續看SDWebImageManager .m
初始化方法

利用image的url生成一個快取時需要的key,cacheKeyFilter的定義如下:
@property (nonatomic, copy) SDWebImageCacheKeyFilterBlock cacheKeyFilter;
typedef NSString *(^SDWebImageCacheKeyFilterBlock)(NSURL *url);
是一個可以返回一個字串的block

檢查一個圖片是否被快取的方法

下面兩個方法比較類似,都是先根據圖片的url建立對應的key

第一個方法先用BOOL isInMemoryCache = ([self.imageCache imageFromMemoryCacheForKey:key] != nil);判斷圖片有沒有在記憶體快取中,如果圖片在記憶體快取中存在,就在主執行緒裡面回撥block,如果圖片沒有在記憶體快取中就去查詢是不是在磁碟快取裡面,然後在主執行緒裡面回到block

第二個方法只查詢圖片是否在磁碟快取裡面,然後在主執行緒裡面回撥block

上面的方法都是用於查詢圖片是否在記憶體快取或磁碟的快取下面的方法可以算是SDWebImageManager核心方法:

首先我們先來看看__block__weak的區別
__block用於指明當前宣告的變數在被block捕獲之後,可以在block中改變變數的值.因為在block宣告的同時會截獲該block所使用的全部自動變數的值,這些值只在block中只有”使用權”而不具有”修改權”.而block說明符就為block提供了變數的修改權,**block不能避免迴圈引用**,這就需要我們在 block 內部將要退出的時候手動釋放掉 blockObj,blockObj = nil

__weak是所有權修飾符,__weak本身是可以避免迴圈引用的問題的,但是其會導致外部物件釋放之後,block內部也訪問不到物件的問題,我們可以通過在block內部宣告一個__strong的變數來指向weakObj,使外部既能在block內部保持住又能避免迴圈引用

我們再來看看SDWebImageCombinedOperation到底有一些什麼內容
SDWebImageCombinedOperation它什麼也不做,儲存了兩個東西(一個block,可以取消下載operation,一個operation,cacheOperation用來下載圖片並且快取的operation)
並且SDWebImageCombineOperation遵循協議,所以operation可以作為返回值返回

@synchronized是OC中一種方便地建立互斥鎖的方式–它可以防止不同執行緒在同一時間執行區塊的程式碼
self.failedURLs是一個NSSet型別的集合,裡面存放的都是下載失敗的圖片的url,failedURLs不是NSArray型別的原因是:
在搜尋一個個元素的時候NSSet比NSArray效率高,主要是它用到了一個演算法hash(雜湊,雜湊) ,比如你要儲存A,一個hash演算法直接就能找到A應該儲存的位置;同樣當你要訪問A的時候,一個hash過程就能找到A儲存的位置,對於NSArray,若想知道A到底在不在陣列中,則需要遍歷整個資料,顯然效率較低了
並且NSSet裡面不含有重複的元素,同一個下載失敗的url只會存在一個
- (BOOL)containsObject:(ObjectType)anObject;,判斷集合裡面是否含有這個obj

把operation加入到self.runningOperations的陣列裡面,並建立一個互斥執行緒鎖來保護這個操作
獲取image的url對應的key

其實看到這裡,下面牽扯的程式碼就會越來越越多了,會牽扯到的類也越來越多,我們先一步步地順著往下看,然後再看涉及到的類裡面寫了些什麼,最後再做總結,在這之前如果你對NSOperation還不夠了解建議你先暫時停下看看下這篇文章NSOperation. 然後再繼續往下閱讀.

- (NSOperation *)queryDiskCacheForKey:(NSString *)key done:(SDWebImageQueryCompletedBlock)doneBlock;SDImageCache的一個方法,根據圖片的key,非同步查詢磁碟快取的方法

我們先來看下這個方法裡面都有什麼:
_ioQueue的定義是:@property (SDDispatchQueueSetterSementics, nonatomic) dispatch_queue_t ioQueue;
_ioQueue的初始化是:
_ioQueue = dispatch_queue_create("com.hackemist.SDWebImageCache", DISPATCH_QUEUE_SERIAL);
DISPATCH_QUEUE_SERIAL代表的是建立一個序列的佇列,所以_ioQueue是一個序列佇列(任務一個執行完畢才執行下一個)
PS:如果你對GCD佇列不太瞭解可以先看下GCD使用經驗與技巧淺談,然後再繼續閱讀額

總結:

這個方法主要完成了這些工作:1.建立一個組合Operation,是一個SDWebImageCombinedOperation物件,這個物件負責對下載operation建立和管理,同時有快取功能,是對下載和快取兩個過程的組合。
2.先去尋找這張圖片 記憶體快取和磁碟快取,這兩個功能在self.imageCachequeryDiskCacheForKey: done:方法中完成,這個方法的返回值既是一個快取operation,最終被賦給上面的OperationcacheOperation屬性。在查詢快取的完成回撥中的程式碼是重點:它會根據是否設定了SDWebImageRefreshCached
選項和代理是否支援下載決定是否要進行下載,並對下載過程中遇到NSURLCache的情況做處理,還有下載失敗的處理以及下載之後進行快取,然後檢視是否設定了形變選項並呼叫代理的形變方法進行對圖片形變處理。
3.將上面的下載方法返回的操作命名為subOperation,並在組合操作operationcancelBlock程式碼塊中新增對subOperationcancel方法的呼叫。

整個大體的操作流程,就是這些,你不感覺少點啥子麼,如何下載,如何快取(快取只是暫時看了兩個相關的方法)我們在下一篇再詳細地看

相關文章