SDWebImage 5.0.1版本 深度解析

地址 github.com/SDWebImage/…git

首先我以爲應該對該框架有一個宏觀層面的瞭解,根據這個思路,層層去分析。github

加載流程

這是官方的時序圖。

咱們看到大體上還有如下的流程。web

1.調用- (void)sd_setImageWithURL:(nullable NSURL *)url(相似的方法有不少種,在UIImageView+WebCache文件中)。數組

2.進入🤝口sd_internalSetImageWithURL(在UIView+WebCach文件中e)。瀏覽器

3.下載圖片。緩存

4.下載以前,先去查找圖片(經過url,先查緩存後查磁盤,也能夠設置查不查緩存)。安全

5.若是有的話,返回結果。bash

6.真正的去下載圖片cookie

7.返回下載的結果。網絡

8.將下載的圖片存在磁盤和緩存中(能夠設置是否存到緩存)。

麻辣雞上次寫的東西 存不上 都沒了、、。從寫一遍吧

一些要記住的枚舉 和 常量##

枚舉採用位運算的方式,能夠選擇多個用 | 隔開。關於位運算,以後再寫。 註釋寫的很清楚了,能夠本身看。

typedef NS_OPTIONS(NSUInteger, SDWebImageOptions) {
    失敗重試,有一個加載失敗的url數組。每次都在數組中查詢一下,
    若是設置失敗重試了,就開始下載。若是設置了失敗不重試,就不下載。
    SDWebImageRetryFailed = 1 << 0,

    低優先級
    SDWebImageLowPriority = 1 << 1,

    只在保存到內存中
    SDWebImageCacheMemoryOnly = 1 << 2,

    漸進下載,相似於瀏覽器的的方式。
    SDWebImageProgressiveDownload = 1 << 3,

    刷新內存,下完完成以後進行比對,若是圖片變了 就刷新緩存中的圖片。
    SDWebImageRefreshCached = 1 << 4,

    後臺下載,當設置了後臺操做的時候,就會自動進行,超期了自動中止
    SDWebImageContinueInBackground = 1 << 5,

    設置cookies 不知道幹啥用的。。
    SDWebImageHandleCookies = 1 << 6,

    容許無效的證書,若是url是https的,可是沒有證書的話,用這個選項。
    SDWebImageAllowInvalidSSLCertificates = 1 << 7,

    高優先級
    SDWebImageHighPriority = 1 << 8,
    
    延遲設置佔位圖
    SDWebImageDelayPlaceholder = 1 << 9,

    轉換動畫圖片,當設置了這個,全部的動畫圖片都會被轉換。
    SDWebImageTransformAnimatedImage = 1 << 10,
    
    取消下載完成以後 自動設置圖片。
    SDWebImageAvoidAutoSetImage = 1 << 11,
    
    取消自動縮放,
    SDWebImageScaleDownLargeImages = 1 << 12,
    
    當圖片在內存中的時候 也去同步查找磁盤。
    SDWebImageQueryDataWhenInMemory = 1 << 13,
    
    一般查找磁盤是異步的,能夠設置成同步的。
    SDWebImageQueryDiskSync = 1 << 14,
    
    只從內存中查找圖片
    SDWebImageFromCacheOnly = 1 << 15,
   
    轉換圖片,好比 webp轉成png。
    SDWebImageForceTransition = 1 << 16
};

typedef NS_OPTIONS(NSUInteger, SDWebImageDownloaderOptions) {
    低優先級
    SDWebImageDownloaderLowPriority = 1 << 0,
    
    漸進下載
    SDWebImageDownloaderProgressiveDownload = 1 << 1,

    使用NSURLCache 不知道幹啥的,。
    SDWebImageDownloaderUseNSURLCache = 1 << 2,

    無視NSURLCache的響應數據
    SDWebImageDownloaderIgnoreCachedResponse = 1 << 3,
    
    後臺下載
    SDWebImageDownloaderContinueInBackground = 1 << 4,

    設置cookies
    SDWebImageDownloaderHandleCookies = 1 << 5,

    容許無效的證書
    SDWebImageDownloaderAllowInvalidSSLCertificates = 1 << 6,

    高優先級
    SDWebImageDownloaderHighPriority = 1 << 7,
    
    自動縮放
    SDWebImageDownloaderScaleDownLargeImages = 1 << 8,
};

typedef NS_ENUM(NSInteger, SDWebImageDownloaderExecutionOrder) {
    
    隊列數據結構 先進先出
    (first-in-first-out).
    SDWebImageDownloaderFIFOExecutionOrder,

    棧結構 後進先出
    (last-in-first-out).
    SDWebImageDownloaderLIFOExecutionOrder
};

typedef NS_ENUM(NSInteger, SDImageCacheType) {
    不緩存
    SDImageCacheTypeNone,
    磁盤緩存
    SDImageCacheTypeDisk,
    內存緩存
    SDImageCacheTypeMemory
};

typedef NS_OPTIONS(NSUInteger, SDImageCacheOptions) {
    在內存中的時候,也去同步查找存盤
    SDImageCacheQueryDataWhenInMemory = 1 << 0,
    同步去查查找磁盤
    SDImageCacheQueryDiskSync = 1 << 1,
    取消自動縮放
    SDImageCacheScaleDownLargeImages = 1 << 2
};

SD_UIKIT
// iOS and tvOS are very similar, UIKit exists on both platforms
#if TARGET_OS_IOS || TARGET_OS_TV
    #define SD_UIKIT 1
#else
    #define SD_UIKIT 0
#endif

信號量當作鎖來用
#define LOCK(lock) dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
#define UNLOCK(lock) dispatch_semaphore_signal(lock);
複製代碼

幾個關鍵的類 簡要說明##

SDImageCache 緩存類

內存緩存採用的是NSCache 和 NSMapTable結合的方式。由於NSCache有的時候會自動釋放,不受控制。我猜NSMapTable是拉鍊法的散列表。

YYCache也是繼承NSCache,可是是用本身實現的雙向鏈表來完成自定義的內存緩存。

SDWebImageDownloader 下載類

SDWebImageManager 核心類

這個類 是下載和查找的入口。是sd的靈魂。

UIView+WebCache UI層面調用的入口 不論是imageView button,最後都會調用到這裏。

根據流程一行一行代碼的看

- (void)sd_setImageWithURL:(nullable NSURL *)url
          placeholderImage:(nullable UIImage *)placeholder
                   options:(SDWebImageOptions)options
                  progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                 completed:(nullable SDExternalCompletionBlock)completedBlock;
    參數沒有什麼好說的了。
    
最終會調用如下方法

代碼段1
- (void)sd_internalSetImageWithURL:(nullable NSURL *)url
                  placeholderImage:(nullable UIImage *)placeholder
                           options:(SDWebImageOptions)options
                      operationKey:(nullable NSString *)operationKey
                     setImageBlock:(nullable SDSetImageBlock)setImageBlock
                          progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                         completed:(nullable SDExternalCompletionBlock)completedBlock
                           context:(nullable NSDictionary<NSString *, id> *)context {
    //設置validOperationKey 
    NSString *validOperationKey = operationKey ?: NSStringFromClass([self class]);
    //經過validOperationKey 看有沒有在下載的隊列中,若是在的話,就取消下載而且從隊列中移除。這裏用了同步鎖來保存線程安安全。
    [self sd_cancelImageLoadOperationWithKey:validOperationKey];
    綁定新的url
    objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    //& 位運算 不知道的同窗查找 按位與 就能夠了
    //若是沒有設置延遲設置佔位圖的話 就馬上設置佔位圖。
    if (!(options & SDWebImageDelayPlaceholder)) {
        dispatch_main_async_safe(^{
        //若是設置了回調 就經過回調把佔位圖傳回去,若果沒設置block
        判斷是否是UIImageView,是的話就直接設置。
            [self sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock];
        });
    }
    
    if (url) {
#if SD_UIKIT
        //若是設置了顯示系統菊花,就顯示出來。
        if ([self sd_showActivityIndicatorView]) {
            [self sd_addActivityIndicator];
        }
#endif
        
        // 重置進度
        self.sd_imageProgress.totalUnitCount = 0;
        self.sd_imageProgress.completedUnitCount = 0;
        
        SDWebImageManager *manager;
        //若是設置了單例mananger以外的mananger就取出來,賦值。
        //sd是容許使用其餘方式初始化的manager的。
        if ([context valueForKey:SDWebImageExternalCustomManagerKey]) {
            manager = (SDWebImageManager *)[context valueForKey:SDWebImageExternalCustomManagerKey];
        } else {
        //若是沒有設置額外的manager ,就獲取單例。
            manager = [SDWebImageManager sharedManager];
        }
        
        __weak __typeof(self)wself = self;
        //進度的block回調
        SDWebImageDownloaderProgressBlock combinedProgressBlock = ^(NSInteger receivedSize, NSInteger expectedSize, NSURL * _Nullable targetURL) {
            wself.sd_imageProgress.totalUnitCount = expectedSize;
            wself.sd_imageProgress.completedUnitCount = receivedSize;
            //若是設置了進度block 就回調回去
            if (progressBlock) {
                progressBlock(receivedSize, expectedSize, targetURL);
            }
        };
        //這個方法的代碼在下一段中貼出來說解。
        //能夠理解爲已經獲得了圖片。
        id <SDWebImageOperation> operation = [manager loadImageWithURL:url options:options progress:combinedProgressBlock completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
            __strong __typeof (wself) sself = wself;
            if (!sself) { return; }
#if SD_UIKIT
            //移除菊花
            [sself sd_removeActivityIndicator];
#endif
            // if the progress not been updated, mark it to complete state
            //若是完成了。且沒有錯誤且總數=0.且完成=0,就把進度和數量設置爲位置的。真的很嚴謹。
            if (finished && !error && sself.sd_imageProgress.totalUnitCount == 0 && sself.sd_imageProgress.completedUnitCount == 0) {
                sself.sd_imageProgress.totalUnitCount = SDWebImageProgressUnitCountUnknown;
                sself.sd_imageProgress.completedUnitCount = SDWebImageProgressUnitCountUnknown;
            }
            //看設置了取消自動賦值嗎
            BOOL shouldCallCompletedBlock = finished || (options & SDWebImageAvoidAutoSetImage);
            //看 需不須要自動賦值
            //第一個判斷條件。有圖片且取消了自動賦值
            //第二個判斷條件。沒圖片且沒有設置延遲設置佔位圖
            BOOL shouldNotSetImage = ((image && (options & SDWebImageAvoidAutoSetImage)) ||
                                      (!image && !(options & SDWebImageDelayPlaceholder)));
            SDWebImageNoParamsBlock callCompletedBlockClojure = ^{
                if (!sself) { return; }
                //要自動設置圖片
                if (!shouldNotSetImage) {
                //把這個view設置成NeedsLayout,在下一個對應mode的Runloop循環的時候,會去從新計算。
                    [sself sd_setNeedsLayout];
                }
                //若是須要回調。
                if (completedBlock && shouldCallCompletedBlock) {
                    completedBlock(image, error, cacheType, url);
                }
            };
            
            // case 1a: we got an image, but the SDWebImageAvoidAutoSetImage flag is set
            // OR
            // case 1b: we got no image and the SDWebImageDelayPlaceholder is not set
            if (shouldNotSetImage) {
            //若是不用去設置圖片 調用block 結束了
                dispatch_main_async_safe(callCompletedBlockClojure);
                return;
            }
            
            UIImage *targetImage = nil;
            NSData *targetData = nil;
            if (image) {
                // case 2a: we got an image and the SDWebImageAvoidAutoSetImage is not set
                //解釋的很清楚了。。
                targetImage = image;
                targetData = data;
            } else if (options & SDWebImageDelayPlaceholder) {
                // case 2b: we got no image and the
                SDWebImageDelayPlaceholder flag is set
                //解釋的很清楚了。。
                targetImage = placeholder;
                targetData = nil;
            }
            
#if SD_UIKIT || SD_MAC
            // check whether we should use the image transition
            SDWebImageTransition *transition = nil;
            //若是設置了圖片轉換或者不緩存,不緩存這個條件我沒懂
            if (finished && (options & SDWebImageForceTransition || cacheType == SDImageCacheTypeNone)) {
                transition = sself.sd_imageTransition;
            }
#endif
            dispatch_main_async_safe(^{
#if SD_UIKIT || SD_MAC
                //設置圖片,設置了轉換的話 作相應的處理。
                [sself sd_setImage:targetImage imageData:targetData basedOnClassOrViaCustomSetImageBlock:setImageBlock transition:transition cacheType:cacheType imageURL:imageURL];
#else
                [sself sd_setImage:targetImage imageData:targetData basedOnClassOrViaCustomSetImageBlock:setImageBlock];
#endif
                //完成的回調
                callCompletedBlockClojure();
            });
        }];
        //處理下載隊列
        [self sd_setImageLoadOperation:operation forKey:validOperationKey];
    } else {
        dispatch_main_async_safe(^{
#if SD_UIKIT
            [self sd_removeActivityIndicator];
#endif
            if (completedBlock) {
                NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}];
                completedBlock(nil, error, SDImageCacheTypeNone, url);
            }
        });
    }
}
複製代碼
代碼段2
這段代碼完成了,查找和下載的操做
- (id <SDWebImageOperation>)loadImageWithURL:(nullable NSURL *)url
                                     options:(SDWebImageOptions)options
                                    progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                                   completed:(nullable SDInternalCompletionBlock)completedBlock {
    // Invoking this method without a completedBlock is pointless
    //斷言 參數不符合的話 就崩潰在這裏。
    NSAssert(completedBlock != nil, @"If you mean to prefetch the image, use -[SDWebImagePrefetcher prefetchURLs] instead");

    //使用者老是有這個錯誤。。。沒辦法。。做者屈服了 加了判斷。
    if ([url isKindOfClass:NSString.class]) {
        url = [NSURL URLWithString:(NSString *)url];
    }

    // 保護程序 防止崩潰 null 就炸了。
    if (![url isKindOfClass:NSURL.class]) {
        url = nil;
    }

    SDWebImageCombinedOperation *operation = [SDWebImageCombinedOperation new];
    operation.manager = self;
    
    //看是否是這個url在不在下載失敗的數組裏
    BOOL isFailedUrl = NO;
    if (url) {
    //用信號量 保證線程安全。
        LOCK(self.failedURLsLock);
        isFailedUrl = [self.failedURLs containsObject:url];
        UNLOCK(self.failedURLsLock);
    }
    //url 沒有值,失敗不重試,在下載失敗的數組中
    if (url.absoluteString.length == 0 || (!(options & SDWebImageRetryFailed) && isFailedUrl)) {
    //完成回調 , 設置error , 返回結束。
        [self callCompletionBlockForOperation:operation completion:completedBlock error:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil] url:url];
        return operation;
    }
    //先鎖,而後再操做隊列中加入本次操做。
    LOCK(self.runningOperationsLock);
    [self.runningOperations addObject:operation];
    UNLOCK(self.runningOperationsLock);
    //經過url 返回key,這個轉化規則,能夠自定義規則。
    NSString *key = [self cacheKeyForURL:url];
    
    SDImageCacheOptions cacheOptions = 0;
    //根據SDWebImageOptions 設置 SDImageCacheOptions。
    // cacheOptions|= SDImageCacheQueryDataWhenInMemory 作位或計算,將結果賦值給cacheOptions
    // 等價於 cacheOptions = cacheOptions | SDImageCacheQueryDataWhenInMemory;
    // 按位或 每一位都進行比較 相同的位都是1 ,結果的那一位就是1.排除其餘的影響。
    if (options & SDWebImageQueryDataWhenInMemory) cacheOptions |= SDImageCacheQueryDataWhenInMemory;
    if (options & SDWebImageQueryDiskSync) cacheOptions |= SDImageCacheQueryDiskSync;
    if (options & SDWebImageScaleDownLargeImages) cacheOptions |= SDImageCacheScaleDownLargeImages;
    
    __weak SDWebImageCombinedOperation *weakOperation = operation;
    //去查找緩存 代碼段3詳細說明。這裏能夠先看作已經查到了。
    operation.cacheOperation = [self.imageCache queryCacheOperationForKey:key options:cacheOptions done:^(UIImage *cachedImage, NSData *cachedData, SDImageCacheType cacheType) {
        __strong __typeof(weakOperation) strongOperation = weakOperation;
        if (!strongOperation || strongOperation.isCancelled) {
        //若是操做被取消了 就安全的從操做隊列中移除。
            [self safelyRemoveOperationFromRunning:strongOperation];
            return;
        }
        
        // 看需不須要從網絡下載圖片。
        //條件1 不是隻從內存中查找。
        //條件2 緩存中沒有圖片,或者刷新內存中的圖片
        //條件3 設置了下載的代理
        BOOL shouldDownload = (!(options & SDWebImageFromCacheOnly))
            && (!cachedImage || options & SDWebImageRefreshCached)
            && (![self.delegate respondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] || [self.delegate imageManager:self shouldDownloadImageForURL:url]);
        //須要下載
        if (shouldDownload) {
            //若是緩存中有圖片且設置了刷新緩存
            if (cachedImage && options & SDWebImageRefreshCached) {
                // If image was found in the cache but SDWebImageRefreshCached is provided, notify about the cached image
                // AND try to re-download it in order to let a chance to NSURLCache to refresh it from server.
                //很詳細 不翻譯了
                [self callCompletionBlockForOperation:strongOperation completion:completedBlock image:cachedImage data:cachedData error:nil cacheType:cacheType finished:YES url:url];
            }

            // download if no image or requested to refresh anyway, and download allowed by delegate
            SDWebImageDownloaderOptions downloaderOptions = 0;
             //根據SDWebImageOptions 設置 SDWebImageDownloaderOptions。
             // cacheOptions|= SDImageCacheQueryDataWhenInMemory   作位或計算,將結果賦值給cacheOptions
             // 等價於 cacheOptions = cacheOptions | SDImageCacheQueryDataWhenInMemory;
             // 按位或 每一位都進行比較 相同的位都是1 ,結果的那一位就是1.排除其餘的影響。
            if (options & SDWebImageLowPriority) downloaderOptions |= SDWebImageDownloaderLowPriority;
            if (options & SDWebImageProgressiveDownload) downloaderOptions |= SDWebImageDownloaderProgressiveDownload;
            if (options & SDWebImageRefreshCached) downloaderOptions |= SDWebImageDownloaderUseNSURLCache;
            if (options & SDWebImageContinueInBackground) downloaderOptions |= SDWebImageDownloaderContinueInBackground;
            if (options & SDWebImageHandleCookies) downloaderOptions |= SDWebImageDownloaderHandleCookies;
            if (options & SDWebImageAllowInvalidSSLCertificates) downloaderOptions |= SDWebImageDownloaderAllowInvalidSSLCertificates;
            if (options & SDWebImageHighPriority) downloaderOptions |= SDWebImageDownloaderHighPriority;
            if (options & SDWebImageScaleDownLargeImages) downloaderOptions |= SDWebImageDownloaderScaleDownLargeImages;
            
            if (cachedImage && options & SDWebImageRefreshCached) {
                // force progressive off if image already cached but forced refreshing
                //downloaderOptions = ~(downloaderOptions &  SDWebImageDownloaderProgressiveDownload)取反
                downloaderOptions &= ~SDWebImageDownloaderProgressiveDownload;
                // ignore image read from NSURLCache if image if cached but force refreshing
                downloaderOptions |= SDWebImageDownloaderIgnoreCachedResponse;
            }
            
            // `SDWebImageCombinedOperation` -> `SDWebImageDownloadToken` -> `downloadOperationCancelToken`, which is a `SDCallbacksDictionary` and retain the completed block below, so we need weak-strong again to avoid retain cycle
            __weak typeof(strongOperation) weakSubOperation = strongOperation;
            //真正開始下載
            strongOperation.downloadToken = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage *downloadedImage, NSData *downloadedData, NSError *error, BOOL finished) {
                __strong typeof(weakSubOperation) strongSubOperation = weakSubOperation;
                //下載操做取消了 什麼也不作 github的ieesu 699 有詳細說明
                if (!strongSubOperation || strongSubOperation.isCancelled) {
                    // Do nothing if the operation was cancelled
                    // See #699 for more details
                    // if we would call the completedBlock, there could be a race condition between this block and another completedBlock for the same object, so if this one is called second, we will overwrite the new data
                } else if (error) {
                     //錯誤了 返回
                    [self callCompletionBlockForOperation:strongSubOperation completion:completedBlock error:error url:url];
                    BOOL shouldBlockFailedURL;
                    // Check whether we should block failed url
                    //若是須要返回錯誤的url
                    if ([self.delegate respondsToSelector:@selector(imageManager:shouldBlockFailedURL:withError:)]) {
                        shouldBlockFailedURL = [self.delegate imageManager:self shouldBlockFailedURL:url withError:error];
                    } else {
                        //看是否有任何錯誤
                        shouldBlockFailedURL = (   error.code != NSURLErrorNotConnectedToInternet
                                                && error.code != NSURLErrorCancelled
                                                && error.code != NSURLErrorTimedOut
                                                && error.code != NSURLErrorInternationalRoamingOff
                                                && error.code != NSURLErrorDataNotAllowed
                                                && error.code != NSURLErrorCannotFindHost
                                                && error.code != NSURLErrorCannotConnectToHost
                                                && error.code != NSURLErrorNetworkConnectionLost);
                    }
                    
                    if (shouldBlockFailedURL) {
                        LOCK(self.failedURLsLock);
                        //添加到失敗url的數組
                        [self.failedURLs addObject:url];
                        UNLOCK(self.failedURLsLock);
                    }
                }
                //沒有錯誤
                else {
                    //設置了失敗重試的話 ,把url 從失敗的數組中移除。
                    if ((options & SDWebImageRetryFailed)) {
                        LOCK(self.failedURLsLock);
                        [self.failedURLs removeObject:url];
                        UNLOCK(self.failedURLsLock);
                    }
                    //是否緩存到磁盤中 只有設置了SDWebImageCacheMemoryOnly 纔不存到磁盤。
                    BOOL cacheOnDisk = !(options & SDWebImageCacheMemoryOnly);
                    
                    // We are done the scale process in SDWebImageDownloader with the shared manager, this is used for custom manager and avoid extra scale.
                    //下載過程當中,已經進行了縮放,若是有自定義的manager ,避免重複的縮放。
                    if (self != [SDWebImageManager sharedManager] && self.cacheKeyFilter && downloadedImage) {
                        downloadedImage = [self scaledImageForKey:key image:downloadedImage];
                    }
                    //NSURLCache 緩存命中
                    if (options & SDWebImageRefreshCached && cachedImage && !downloadedImage) {
                        // Image refresh hit the NSURLCache cache, do not call the completion block
                        //若是圖片下載完成 且 下載的圖片只有一個 或者 設置了SDWebImageTransformAnimatedImage
                    } else if (downloadedImage && (!downloadedImage.images || (options & SDWebImageTransformAnimatedImage)) && [self.delegate respondsToSelector:@selector(imageManager:transformDownloadedImage:withURL:)]) {
                       
                       //出來圖片轉換以後的狀況
                        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
                            UIImage *transformedImage = [self.delegate imageManager:self transformDownloadedImage:downloadedImage withURL:url];

                            if (transformedImage && finished) {
                                //檢查圖片有沒有被編輯過
                                BOOL imageWasTransformed = ![transformedImage isEqual:downloadedImage];
                                NSData *cacheData;
                                // pass nil if the image was transformed, so we can recalculate the data from the image
                                // 若是設置了全局的解析轉換
                                if (self.cacheSerializer) {
                                    cacheData = self.cacheSerializer(transformedImage, (imageWasTransformed ? nil : downloadedData), url);
                                } else {
                                    cacheData = (imageWasTransformed ? nil : downloadedData);
                                }
                                //緩存  請看代碼段4
                                [self.imageCache storeImage:transformedImage imageData:cacheData forKey:key toDisk:cacheOnDisk completion:nil];
                            }
                            //完成的block回調
                            [self callCompletionBlockForOperation:strongSubOperation completion:completedBlock image:transformedImage data:downloadedData error:nil cacheType:SDImageCacheTypeNone finished:finished url:url];
                        });
                    } else {
                        //下載圖片成功 且 完成
                        if (downloadedImage && finished) {
                            if (self.cacheSerializer) {
                                dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
                                    NSData *cacheData = self.cacheSerializer(downloadedImage, downloadedData, url);
                                    [self.imageCache storeImage:downloadedImage imageData:cacheData forKey:key toDisk:cacheOnDisk completion:nil];
                                });
                            } else {
                                //保存內存和磁盤
                                [self.imageCache storeImage:downloadedImage imageData:downloadedData forKey:key toDisk:cacheOnDisk completion:nil];
                            }
                        }
                        [self callCompletionBlockForOperation:strongSubOperation completion:completedBlock image:downloadedImage data:downloadedData error:nil cacheType:SDImageCacheTypeNone finished:finished url:url];
                    }
                }

                if (finished) {
                    [self safelyRemoveOperationFromRunning:strongSubOperation];
                }
            }];
            //圖片在內存中
        } else if (cachedImage) {
            [self callCompletionBlockForOperation:strongOperation completion:completedBlock image:cachedImage data:cachedData error:nil cacheType:cacheType finished:YES url:url];
            [self safelyRemoveOperationFromRunning:strongOperation];
        } else {
            // Image not in cache and download disallowed by delegate
            [self callCompletionBlockForOperation:strongOperation completion:completedBlock image:nil data:nil error:nil cacheType:SDImageCacheTypeNone finished:YES url:url];
            [self safelyRemoveOperationFromRunning:strongOperation];
        }
    }];

    return operation;
}


複製代碼
代碼段3 下載
- (nullable SDWebImageDownloadToken *)downloadImageWithURL:(nullable NSURL *)url
                                                   options:(SDWebImageDownloaderOptions)options
                                                  progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                                                 completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock {
    __weak SDWebImageDownloader *wself = self;

    return [self addProgressCallback:progressBlock completedBlock:completedBlock forURL:url createCallback:^SDWebImageDownloaderOperation *{
        __strong __typeof (wself) sself = wself;
        NSTimeInterval timeoutInterval = sself.downloadTimeout;
        if (timeoutInterval == 0.0) {
            timeoutInterval = 15.0;
        }

        // In order to prevent from potential duplicate caching (NSURLCache + SDImageCache) we disable the cache for image requests if told otherwise
        NSURLRequestCachePolicy cachePolicy = options & SDWebImageDownloaderUseNSURLCache ? NSURLRequestUseProtocolCachePolicy : NSURLRequestReloadIgnoringLocalCacheData;
        NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url
                                                                    cachePolicy:cachePolicy
                                                                timeoutInterval:timeoutInterval];
        
        request.HTTPShouldHandleCookies = (options & SDWebImageDownloaderHandleCookies);
        request.HTTPShouldUsePipelining = YES;
        if (sself.headersFilter) {
            request.allHTTPHeaderFields = sself.headersFilter(url, [sself allHTTPHeaderFields]);
        }
        else {
            request.allHTTPHeaderFields = [sself allHTTPHeaderFields];
        }
        SDWebImageDownloaderOperation *operation = [[sself.operationClass alloc] initWithRequest:request inSession:sself.session options:options];
        operation.shouldDecompressImages = sself.shouldDecompressImages;
        
        if (sself.urlCredential) {
            operation.credential = sself.urlCredential;
        } else if (sself.username && sself.password) {
            operation.credential = [NSURLCredential credentialWithUser:sself.username password:sself.password persistence:NSURLCredentialPersistenceForSession];
        }
        
        if (options & SDWebImageDownloaderHighPriority) {
            operation.queuePriority = NSOperationQueuePriorityHigh;
        } else if (options & SDWebImageDownloaderLowPriority) {
            operation.queuePriority = NSOperationQueuePriorityLow;
        }
        
        if (sself.executionOrder == SDWebImageDownloaderLIFOExecutionOrder) {
            // Emulate LIFO execution order by systematically adding new operations as last operation's dependency [sself.lastAddedOperation addDependency:operation]; sself.lastAddedOperation = operation; } return operation; }]; } 複製代碼
代碼段4 緩存

- (void)storeImage:(nullable UIImage *)image
         imageData:(nullable NSData *)imageData
            forKey:(nullable NSString *)key
            toDisk:(BOOL)toDisk
        completion:(nullable SDWebImageNoParamsBlock)completionBlock {
    if (!image || !key) {
        if (completionBlock) {
            completionBlock();
        }
        return;
    }
    // if memory cache is enabled
    if (self.config.shouldCacheImagesInMemory) {
        //計算大小
        NSUInteger cost = SDCacheCostForImage(image);
        [self.memCache setObject:image forKey:key cost:cost];
    }
    
    if (toDisk) {
        //ioQueue 隊列 先進先出 一個一個來 保證順序
        dispatch_async(self.ioQueue, ^{
            @autoreleasepool {
                NSData *data = imageData;
                if (!data && image) {
                    // If we do not have any data to detect image format, check whether it contains alpha channel to use PNG or JPEG format
                    SDImageFormat format;
                    if (SDCGImageRefContainsAlpha(image.CGImage)) {
                        format = SDImageFormatPNG;
                    } else {
                        format = SDImageFormatJPEG;
                    }
                    data = [[SDWebImageCodersManager sharedInstance] encodedDataWithImage:image format:format];
                }
                [self _storeImageDataToDisk:data forKey:key];
            }
            
            if (completionBlock) {
                dispatch_async(dispatch_get_main_queue(), ^{
                    completionBlock();
                });
            }
        });
    } else {
        if (completionBlock) {
            completionBlock();
        }
    }
}

複製代碼
相關文章
相關標籤/搜索