SDWebImage 源碼分析

主要圍繞三個方面來闡述git

  • 加載流程
  • 緩存模塊
  • 下載模塊

加載流程

這是從 github SDWebImage 地址下載的圖. 圖片說明了整個圖片加載的時序.

  1. 首先是根據地址去緩存中取圖片,此處的緩存是雙緩存(磁盤 + 內存)
  2. 若是找到了就交付到上層處理/顯示
  3. 若是沒有都沒找到,就會去網絡下載圖片
  4. 下載完了交付上層處理/顯示
  5. 另外對下載的圖片進行緩存

下面根這源碼來驗證一下上面說的知識 根據 Github 上面提供的整個 SD 的類圖可知,主要是 SDWebImageManager 在操做管理查找流程. 咱們就根據你們使用的最多的方法, 來一步步跟蹤.github

  1. 一般狀況下,咱們使用這個方法對圖片進行加載。
UIImageView+WebCache.h 文件中
-(void)sd_setImageWithURL:(nullable NSURL *)url
          placeholderImage:(nullable UIImage *)placeholder
複製代碼
  1. 查找 sd_internalSetImageWithURL 方法.
UIView+WebCache.h 文件中
sd_internalSetImageWithURL ....
複製代碼
  1. 若是你設置了 PlaceholderImage, 他會先去顯示你的 placeholderImage
if (!(options & SDWebImageDelayPlaceholder)) {
        dispatch_main_async_safe(^{
            [self sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock cacheType:SDImageCacheTypeNone imageURL:url];
        });
    }
複製代碼
  1. 繼續去 SDWebImageManager 中的 loadImageWithURL 查找圖片
id <SDWebImageOperation> operation = [manager loadImageWithURL:url options:options context:context progress:combinedProgressBlock completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
複製代碼
  1. 而後調用 callCacheProcessForOperation 進行緩存查找操做
BOOL shouldQueryCache = (options & SDWebImageFromLoaderOnly) == 0;
if (shouldQueryCache) {
	// 緩存查找
}else {
	// 進行下載操做
}
複製代碼
  1. 接上緩存查找過程, 回去 SDImageCache 中的 queryImageForKey 方法繼續緩存查找
-(id<SDWebImageOperation>)queryImageForKey:(NSString *)key options:(SDWebImageOptions)options context:(nullable SDWebImageContext *)context completion:(nullable SDImageCacheQueryCompletionBlock)completionBlock {
複製代碼
  1. 接下來到 queryCacheOperationForKey 方法緩存

    • 首先去內存中找,若是有就用 Block 的方式傳遞到上層顯示
    // First check the in-memory cache...
    UIImage *image = [self imageFromMemoryCacheForKey:key];
    複製代碼
    • 不然就去 Disk 查找
    NSData *diskData = [self diskImageDataBySearchingAllPathsForKey:key];
    複製代碼
    • 若是 Disk 有就返回,而且把這個值存入 MemoryCache 中,這樣就能夠再下次查找中更快的找到對應的圖片信息
    [self.memCache setObject:diskImage forKey:key cost:cost];
    複製代碼
  2. 若是沒找到就去下載圖片bash

callDownloadProcessForOperation
複製代碼
  1. 進入下載流程, 生成 SDWebImageDownloadToken 對象.就行下載
[self callStoreCacheProcessForOperation:operation url:url options:options context:context downloadedImage:downloadedImage downloadedData:downloadedData finished:finished progress:progressBlock completed:completedBlock];
複製代碼
  1. 下載完成就行存儲
[self.imageCache storeImage:transformedImage imageData:cacheData forKey:cacheKey cacheType:storeCacheType completion:nil];
複製代碼
* 分別存儲到內存和磁盤
```
-(void)storeImage:(nullable UIImage *)image
     imageData:(nullable NSData *)imageData
        forKey:(nullable NSString *)key
      toMemory:(BOOL)toMemory
        toDisk:(BOOL)toDisk
    completion:(nullable SDWebImageNoParamsBlock)completionBlock {
``` 
複製代碼
  1. 返回數據顯示
[self callCompletionBlockForOperation:operation completion:completedBlock image:transformedImage data:downloadedData error:nil cacheType:SDImageCacheTypeNone finished:finished url:url];
複製代碼

到此整個圖片加載流程就完了.網絡


緩存模塊

下圖是最新的 SDWebImage 的文件結構,這裏主要看看 Cache 板塊session

  • 首先介紹這些文件是幹啥的(記得以前的 Cache 沒這麼多文件)
    • SDImageCacheConfig.h SDImageCacheConfig.m 這兩個文件是來作一些設置
    • SDMemoryCache.h SDMemoryCache.m 是內存緩存相關的操做
    • SDDiskCache.h SDDiskCache.m 是磁盤緩存相關的操做
    • SDImageCacheDefine.h SDImageCacheDefine.m 主要是爲 SDImageCache 定義了一些協議
    • SDImageCachesManager.h SDImageCachesManager.m 主要對 Cache 刪除和添加
    • SDImageCache.h SDImageCache.m 操做 SDDiskCacheSDMemoryCache
首先先看設置文件 SDImageCacheConfig
* 定義了默認的磁盤緩存時間, 默認爲 1 周 `kDefaultCacheMaxDiskAge = 60 * 60 * 24 *7;`
* shouldDisableiCloud 是否使用 iCloud
* shouldCacheImagesInMemory 是否使用內存緩存
* shouldUseWeakMemoryCache 是否使用弱應用內存緩存
* shouldRemoveExpiredDataWhenEnterBackground 是否刪除超過日期的數據
* maxDiskAge 最大磁盤緩存週期爲 `1` 周
* maxDiskSize 最大磁盤緩存大小.默認爲 0,意味着沒有大小限制
* maxMemoryCost 內存緩存大小,默認爲 0,意味着沒有大小限制
* maxMemoryCount 內存緩存的最大數量,默認 0
* diskCacheExpireType  磁盤緩存失效的類型(用於刪除使用)
複製代碼
SDMemoryCache
* `SDMemoryCache` 是繼承自 系統的 `NSCache`.
* 這個類主要是操做內存,刪除,保存的操做
* 首先註冊了內存警告通知 `UIApplicationDidReceiveMemoryWarningNotification`
* 保存
	- (void)setObject:(id)obj forKey:(id)key cost:(NSUInteger)g {
		[super setObject:obj forKey:key cost:g];
		if (!self.config.shouldUseWeakMemoryCache) {
    		return;
	    }
	    if (key && obj) {
        // Store weak cache
    		SD_LOCK(self.weakCacheLock);
        [self.weakCache setObject:obj forKey:key];
	      SD_UNLOCK(self.weakCacheLock);
	    }
	}
	
	Note: 這裏的保存方法是作了兩次保存操做
		1. 使用 [Super setObject:obj forKey:key cost: g] 先給父類設置,也就是給系統的 NSCache 設置緩存
		2. 而後在給當前緩存內的 `NSMapTable` 設置值
		3. 這樣也就是拿空間換時間的操做,至關於在內存中是有兩份數據的,若是第一份沒有,就去拿第二份,這樣就保證了快速度的響應. 

* 取值
	-(id)objectForKey:(id)key {
		 id obj = [super objectForKey:key];
	    if (!self.config.shouldUseWeakMemoryCache) {
		      return obj;
	    }
		  if (key && !obj) {
 		   // Check weak cache
 	   	SD_LOCK(self.weakCacheLock);
     	   obj = [self.weakCache objectForKey:key];
   	   SD_UNLOCK(self.weakCacheLock);
   	   if (obj) {
          // Sync cache
          NSUInteger cost = 0;
          if ([obj isKindOfClass:[UIImage class]]) {
              cost = [(UIImage *)obj sd_memoryCost];
          }
          [super setObject:obj forKey:key cost:cost];
      }
	    }
  	  return obj;
	}
	1. 先去 `super` 取值
	2. 若是木有就去 `NSMapTable` 中取值
複製代碼
SDDiskCache
* 使用 NSFileManager 管理圖片緩存
* 根據 `key` 在它 SDDiskCacheFileNameForKey 方法中,使用 `CC_MD5` 生成一個 新的  `Key`, 轉換成 url,存放 data.
* 還有一點就是清理過時的數據,有兩種方式
	1. SDImageCacheConfigExpireTypeAccessDate 根據訪問時間
	2. SDImageCacheConfigExpireTypeModificationDate 修改時間(默認)
	3. 根據 self.config.maxDiskAge 來對比刪除超過期間的圖片
	4. 根據 self.config.maxDiskSize 來刪除磁盤緩存的數據,清理到self.config.maxDiskSize /2 爲止.
複製代碼
SDImageCachesManager
* 主要是操做 `SDImageCache` 類,對緩存就行 `存儲`, `刪除`, `清理`, `查詢`
複製代碼
SDImageCacheConfig
* 主要是對圖片的 `Decode` 操做
複製代碼
SDImageCache
* 值得說的是,他在這裏監聽了兩個系統通知
1. UIApplicationWillTerminateNotification
2. UIApplicationDidEnterBackgroundNotification
當這兩個方法執行時,實際上是作了一件事情,就是清理磁盤緩存 
[self.diskCache removeExpiredData];
複製代碼

下載模塊

SDWebImageDownloaderConfig
* 設置最大併發數 maxConcurrentDownloads ,默認是 6
* 設置下載超時時間 downloadTimeout ,默認 15s
* 下載執行順序 executionOrder , 默認先進先出
複製代碼
SDWebImageDownloader
Note: 總結就是 使用 系統 NSURLSession 建立 task ,在本類中它實現了 NSURLSessionTaskDelegate, 當數據下載完成,就去顯示.
	* 主要是設置下載隊列
  	* 拼接 HTTPHeader 
	* 組裝 SDWebImageDownloaderOperation 
  	* 設置 Credential
	* 配置 NSOperation 的優先級
	* 返回一個 SDWebImageDownloaderOperation
	* 雖然設置了 NSURLSessionTaskDelegate 的代理在這個類中,可是實際仍是把結果交費給了 SDWebImageDownloaderOperation, 統一處理
	
	NSOperation<SDWebImageDownloaderOperation> *dataOperation = [self operationWithTask:dataTask];
    if ([dataOperation respondsToSelector:@selector(URLSession:dataTask:didReceiveResponse:completionHandler:)]) {
    		[dataOperation URLSession:session dataTask:dataTask didReceiveResponse:response completionHandler:completionHandler];
	 } else {
  	  if (completionHandler) {
     	   completionHandler(NSURLSessionResponseAllow);
   		 }
	}
複製代碼
SDWebImageDownloaderOperation
Note: 他是集成自 NSOperation, 重寫了 Start 方法
	* 在 Start 時,若是 app 進入後臺,就取消下載.
	* 設置 NSURLSession 以後就下載任務
	* 其中手動(KVC)出發了 isFinished isExecuting
	* 而後就是在 NSURLSessionTaskDelegate,NSURLSessionDataDelegate,設置顯示就回調
複製代碼
相關文章
相關標籤/搜索