搬好小板凳看SDWebImage源码解析(一)

看了下离开上次写简书博客的命宫,已经仙逝了多个多月了,很羞愧。正好最近项目不忙,抽点时间研商下第三方库,朋友提议总计写成博客就这么开篇了。内容篇幅会相比长,所以指望各位看官搬好小板凳看SDWebImage源码解析,假如没有定性真的是很难坚定不移下去。希望我们可以坚贞不屈跟着博主一块学完SDWebImage源码连串。

一.准备知识

在专业学习源码前,先讲一些SDWebImage中用到的生僻知识点,有些用的很频繁,可是不少人对这一个知识点模糊不清,如若不搞清楚会大大影响阅读效用,比如枚举NS_OPTIONS的二进制位运算。

1> NS_OPTIONS与位运算

NS_OPTIONS用来定义位移相关操作的枚举值,当一个枚举变量需要携带多种值的时候就需要,我们得以参考UIKit.Framework的头文件,可以看出大量的枚举定义。例如在SDWebImage下边就会接触到SDWebImageOptions枚举值:

typedef NS_OPTIONS(NSUInteger, SDWebImageOptions) {
    SDWebImageRetryFailed = 1 << 0,
    SDWebImageLowPriority = 1 << 1,
    SDWebImageCacheMemoryOnly = 1 << 2,
    SDWebImageProgressiveDownload = 1 << 3,
    SDWebImageRefreshCached = 1 << 4,
    SDWebImageContinueInBackground = 1 << 5,
    SDWebImageHandleCookies = 1 << 6,
    SDWebImageAllowInvalidSSLCertificates = 1 << 7,
    SDWebImageHighPriority = 1 << 8,
    SDWebImageDelayPlaceholder = 1 << 9,
    SDWebImageTransformAnimatedImage = 1 << 10,
    SDWebImageAvoidAutoSetImage = 1 << 11,
    SDWebImageScaleDownLargeImages = 1 << 12
};

“<<”是位运算中的左移运算符,第一个值SDWebImageRetryFailed = 1
<<
0,十进制1转化为二进制:0b00000001,这里<<0将所有二进制位左移0位,那么依然0b00000001,最后SDWebImageRetryFailed
值为1.
其次个枚举值SDWebImageLowPriority
=1<<1,这里是将1的二进制所有位向左移动1位,空缺的用0补齐,那么0b00000001变成0b00000010,十进制为2则SDWebImageLowPriority值为2。

左移1位示意图

逐一类推:
SDWebImageCacheMemoryOnly向左移动2位等于4,
SDWebImageProgressiveDownload向左移动3位等于8.
下边写一个,customImageView是我们自定义的imageView实例,在SDWebImage的SDWebImageManager.m具体应用中:

   [customImageView sd_setImageWithURL:url placeholderImage:nil options:SDWebImageRetryFailed | SDWebImageCacheMemoryOnly];

专注到代码中用到了”|”,‘|’是位运算中的或运算,需要多个操作数,成效是将三个数的一律位展开逻辑或运算,即只要五个对应位有一个位1,则运算后此位为1,倘使多少个对应位都为0。例如十进制1的二进制0b00000001
| 十进制2的二进制0b00000010,结果为0b00000011十进制为3。下图示例:

或运算

当options值为SDWebImageRetryFailed |
SDWebImageCacheMemoryOnly时,执行或运算0b00000001| 0b00000100 =
0b00000101 十进制是5.
这就是说在具体的形式内部options怎么利用呢?上边的代码SD将options和SDWebImageRetryFailed做”&”运算:

    if (url.absoluteString.length == 0 || (!(options & SDWebImageRetryFailed) && isFailedUrl)) {
    }

‘&’是位运算中的与运算,当对应位数同为1结果才为1.诸如十进制1的二进制0b00000001&十进制2的二进制0b00000010,结果为0b00000000十进制是0.

与运算

下边代码中,SD将此时的options二进制0b00000101和SDWebImageRetryFailed的二进制举行&运算,假使options包含了SDWebImageRetryFailed则结果为真。SD通篇都遵照options做了广大政工,因而了解可选枚举和位运算至极重大。

2> NSURLCredential

当移动端和服务器在传输过程中,服务端有可能在回去Response时顺手表达,询问
HTTP
请求的发起方是什么人,这时候发起方应提供正确的用户名和密码(即认证信息)。这时候就需要NSURLCredential身份认证,更加切实可以查阅这篇博客

3>涉及的宏定义

3-1>FOUNDATION_EXPORT:

用来定义常量,和#define效用一样,只可是在检测字符串的值是否等于是比#define的频率更高,因为正如的是指针地址。

3-2>NS_DESIGNATED_INITIALIZER :

NS_DESIGNATED_INITIALIZER宏来实现指定构造器,通常是想告知调用者要用这些艺术去初步化类对象,便于规范API。

3-2>__deprecated_msg

用来指示此方法或性质已经撤除。

@property (nonatomic, assign) BOOL shouldUseCredentialStorage __deprecated_msg("Property deprecated. Does nothing. Kept only for backwards compatibility");

4>initialize

initialize静态方法会在第一次使用该类在此之前由运行期系统调用,而且仅调用四回,属于懒加载范畴,假诺不行使则不会调用,可以在艺术内部做一些起首化操作,但是load方法是假诺开动程序就会调用。关于initialize和load更加详实的看这里

5>dispatch_barrier_sync

GCD中的知识点,承上启下,当把任务A添加到队列中运用dispatch_barrier_sync时,它会等待在它面前插入队列的天职先举行完,然后等职责履行完再实行前面的职责。更加详实的可点这里

如今整理了这多少个,假如还有其余必要讲的话,下文会再做表明。大家有不谙习的知识点也足以在评头论足中平复,我会挑痛点相比较多的及时更新到博客中。

二.大旨源码解析

SDWebImage的核心Workflow:

SDWebImage-Workflow

咱俩要探究的源码首虽然环绕那一个基本类进行。
读书提出TIPS:SDWebImage的源码多,逻辑复杂,我的提出是读者下载一份源码,源码和本文同步阅读,因为只要没看过源码的调用逻辑,单看本文的解读不会形成系统。会在源码上加注释,并且copy到随笔中,运行品种参考调用栈配合本文效果会更好。

1>UIImageView+WebCache/UIView+WebCache

UIImageView+WebCache对外使用的API入口,这么些类的接口设计把设计形式五大规格之一的接口分离原则反映的淋漓尽致。首先说一下哪些是接口分离原则:
接口分离原则:为特定功用提供特定的接口,不要选用单一的总接口包括所有机能,而是应当依据效用把这多少个接口分割,裁减依赖,不可以迫使用户去看重这一个他们不利用的接口。
在.h中得以看出:

- (void)sd_setImageWithURL:(nullable NSURL *)url NS_REFINED_FOR_SWIFT;
- (void)sd_setImageWithURL:(nullable NSURL *)url
          placeholderImage:(nullable UIImage *)placeholder;
- (void)sd_setImageWithURL:(nullable NSURL *)url
          placeholderImage:(nullable UIImage *)placeholder
                   options:(SDWebImageOptions)options

这么调用者需要哪些功能就去调用特定的API,清晰易增添,在.m中会企划一个总的接口包含所有机能。UIImageView+WebCache重如果一个接口,没有太多需要研商的,在.m中总接口中又调用了UIView的扩大方法,接下去讲一下UIView+WebCache。
UIView+WebCache提供了实际的图形加载请求,UIButton和UIImageView都可调用sd_internalSetImageWithURL来落实,下边具体看下sd_internalSetImageWithURL的兑现。具体的源码意思都做了诠释:

- (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 *)context {
    //根据参数operationKey取消当前类所对应的下载Operation对象,如果operationKey为nil key取NSStringFromClass([self class])
    NSString *validOperationKey = operationKey ?: NSStringFromClass([self class]);
    //具体的取消操作在UIView+WebCacheOperation中实现
    [self sd_cancelImageLoadOperationWithKey:validOperationKey];

    //利用关联对象给当前self实例绑定url key=imageURLKey value=url
    objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    //这里就用到了我们开篇讲的位运算,利用&与运算判断调用者是否需要设置占位图,需要则set
    if (!(options & SDWebImageDelayPlaceholder)) {
        dispatch_main_async_safe(^{
            [self sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock];
        });
    }

    if (url) {
        // check if activityView is enabled or not
        // 判断之前是否利用关联对象给self设置了显示菊花加载,如果有则add
        if ([self sd_showActivityIndicatorView]) {
            [self sd_addActivityIndicator];
        }

        __weak __typeof(self)wself = self;
        //调用SDWebImageManager的loadImageWithURL方法去加载图片,返回值是SDWebImageCombinedOperation
        id <SDWebImageOperation> operation = [SDWebImageManager.sharedManager loadImageWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
            __strong __typeof (wself) sself = wself;
            //在这里移除菊花
            [sself sd_removeActivityIndicator];
            if (!sself) { return; }
            BOOL shouldCallCompletedBlock = finished || (options & SDWebImageAvoidAutoSetImage);
            //是否不显示图片两个条件满足其一即可 1>调用者手动主动配置,哪怕image不为nil 2>没有图片并且不delaye占位图情况
            BOOL shouldNotSetImage = ((image && (options & SDWebImageAvoidAutoSetImage)) ||
                                      (!image && !(options & SDWebImageDelayPlaceholder)));
            SDWebImageNoParamsBlock callCompletedBlockClojure = ^{
                if (!sself) { return; }
                if (!shouldNotSetImage) {
                    [sself sd_setNeedsLayout];
                }
                //如果设置了不自动显示图片,则回调让调用者手动添加显示图片 程序return
                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) {//如果设置了不自动显示图片,则回调让调用者手动添加显示图片 程序return
                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) {//如果没有image,并且调用者设置了delaye显示默认图那这里targetImage设置为placeholder
                // case 2b: we got no image and the SDWebImageDelayPlaceholder flag is set
                targetImage = placeholder;
                targetData = nil;
            }
            BOOL shouldUseGlobalQueue = NO;
            //外部参数context如果设置了全局队列中setImage,那shouldUseGlobalQueue为YES,否则默认在dispatch_get_main_queue
            if (context && [context valueForKey:SDWebImageInternalSetImageInGlobalQueueKey]) {
                shouldUseGlobalQueue = [[context valueForKey:SDWebImageInternalSetImageInGlobalQueueKey] boolValue];
            }
            dispatch_queue_t targetQueue = shouldUseGlobalQueue ? dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0) : dispatch_get_main_queue();

            dispatch_queue_async_safe(targetQueue, ^{//队列中设置image给imageView或者button
                [sself sd_setImage:targetImage imageData:targetData basedOnClassOrViaCustomSetImageBlock:setImageBlock];
                dispatch_main_async_safe(callCompletedBlockClojure);
            });
        }];
        //绑定operation到当前self,key=validOperationKey,value=operation
        [self sd_setImageLoadOperation:operation forKey:validOperationKey];
    } else {v
        dispatch_main_async_safe(^{
            //移除菊花 抛出url为nil的回调
            [self sd_removeActivityIndicator];
            if (completedBlock) {
                NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}];
                completedBlock(nil, error, SDImageCacheTypeNone, url);
            }
        });
    }
}

下面是在这一历程的大概流程图协理通晓:

2>SDWebImageManager

SDWebImageManager类是SDWebImage中的核心类,紧要承担调用SDWebImageDownloader举办图片下载,以及在下载之后拔取SDImageCache举行图纸缓存。并且此类还足以跳过UIImageViewe/Cache或者UIView/Cache单独使用,不仅局限于一个UIView。
SDWebImageManager.h注解:

@class SDWebImageManager;

@protocol SDWebImageManagerDelegate <NSObject>

@optional

//当缓存没有发现当前图片,那么会查看调用者是否实现改方法,如果return一个no,则不会继续下载这张图片
- (BOOL)imageManager:(nonnull SDWebImageManager *)imageManager shouldDownloadImageForURL:(nullable NSURL *)imageURL;

//当图片下载完成但是未添加到缓存里面,这时候调用该方法可以给图片旋转方向,注意是异步执行, 防止组织主线程
- (nullable UIImage *)imageManager:(nonnull SDWebImageManager *)imageManager transformDownloadedImage:(nullable UIImage *)image withURL:(nullable NSURL *)imageURL;

@end

@interface SDWebImageManager : NSObject
//SDWebImageManagerDelegate的delegate
@property (weak, nonatomic, nullable) id <SDWebImageManagerDelegate> delegate;
//缓存中心
@property (strong, nonatomic, readonly, nullable) SDImageCache *imageCache;
//下载中心
@property (strong, nonatomic, readonly, nullable) SDWebImageDownloader *imageDownloader;
//这个缓存block的作用是,在block内部进行缓存key的生成并return,key就是根据图片url根据规则生成,sd的缓存策略就是key是图片url,value就是image
@property (nonatomic, copy, nullable) SDWebImageCacheKeyFilterBlock cacheKeyFilter;
//返回SDWebImageManager的单例
+ (nonnull instancetype)sharedManager;
//根据特定的cache和downloader生成一个新的SDWebImageManager
- (nonnull instancetype)initWithCache:(nonnull SDImageCache *)cache downloader:(nonnull SDWebImageDownloader *)downloader NS_DESIGNATED_INITIALIZER;
//下载图片的关键方法,第一个参数图片url,第二个参数设置下载多样操作,第三个参数下载中进度block,第四个参数下载完成后回调
- (nullable id <SDWebImageOperation>)loadImageWithURL:(nullable NSURL *)url
                                              options:(SDWebImageOptions)options
                                             progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                                            completed:(nullable SDInternalCompletionBlock)completedBlock;
//缓存图片根据指定的url和image
- (void)saveImageToCache:(nullable UIImage *)image forURL:(nullable NSURL *)url;
//取消所有当前的operation
- (void)cancelAll;
//检查是否有图片正在下载
- (BOOL)isRunning;
//异步检查图片是否已经缓存
- (void)cachedImageExistsForURL:(nullable NSURL *)url
                     completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock;
//检查图片是否缓存 在磁盘中
- (void)diskImageExistsForURL:(nullable NSURL *)url
                   completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock;
//给定一个url返回缓存的字符串key
- (nullable NSString *)cacheKeyForURL:(nullable NSURL *)url;

@end

SDWebImageManager.m注解:

@interface SDWebImageCombinedOperation : NSObject <SDWebImageOperation>
//是否取消当前所有操作
@property (assign, nonatomic, getter = isCancelled) BOOL cancelled;
//没有参数取消回调
@property (copy, nonatomic, nullable) SDWebImageNoParamsBlock cancelBlock;
//执行缓存的操作
@property (strong, nonatomic, nullable) NSOperation *cacheOperation;

@end

@interface SDWebImageManager ()
//缓存对象
@property (strong, nonatomic, readwrite, nonnull) SDImageCache *imageCache;
//下载对象
@property (strong, nonatomic, readwrite, nonnull) SDWebImageDownloader *imageDownloader;
//集合存储所有下载失败的图片url
@property (strong, nonatomic, nonnull) NSMutableSet<NSURL *> *failedURLs;
//存储正在执行下载图片操作的数组
@property (strong, nonatomic, nonnull) NSMutableArray<SDWebImageCombinedOperation *> *runningOperations;

@end

@implementation SDWebImageManager
//生成一个SDWebImagemanager的单例
+ (nonnull instancetype)sharedManager {
    static dispatch_once_t once;
    static id instance;
    dispatch_once(&once, ^{
        instance = [self new];
    });
    return instance;
}
//初始化SDImageCache/SDWebImageDownloade
- (nonnull instancetype)init {
    SDImageCache *cache = [SDImageCache sharedImageCache];
    SDWebImageDownloader *downloader = [SDWebImageDownloader sharedDownloader];
    return [self initWithCache:cache downloader:downloader];
}
//初始化以及属性绑定
- (nonnull instancetype)initWithCache:(nonnull SDImageCache *)cache downloader:(nonnull SDWebImageDownloader *)downloader {
    if ((self = [super init])) {
        _imageCache = cache;
        _imageDownloader = downloader;
        _failedURLs = [NSMutableSet new];
        _runningOperations = [NSMutableArray new];
    }
    return self;
}
//根据URL获取缓存中的key
- (nullable NSString *)cacheKeyForURL:(nullable NSURL *)url {
    if (!url) {
        return @"";
    }

    if (self.cacheKeyFilter) {
        return self.cacheKeyFilter(url);
    } else {
        return url.absoluteString;
    }
}
//检查缓存中是否缓存了当前url对应的图片-先判断内存缓存、再判断磁盘缓存
- (void)cachedImageExistsForURL:(nullable NSURL *)url
                     completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock {
    NSString *key = [self cacheKeyForURL:url];
    //判断内存缓存是否存在
    BOOL isInMemoryCache = ([self.imageCache imageFromMemoryCacheForKey:key] != nil);

    if (isInMemoryCache) {
        // making sure we call the completion block on the main queue
        dispatch_async(dispatch_get_main_queue(), ^{
            if (completionBlock) {
                completionBlock(YES);
            }
        });
        return;
    }
    //判断磁盘缓存中是否存在
    [self.imageCache diskImageExistsWithKey:key completion:^(BOOL isInDiskCache) {
        // the completion block of checkDiskCacheForImageWithKey:completion: is always called on the main queue, no need to further dispatch
        if (completionBlock) {
            completionBlock(isInDiskCache);
        }
    }];
}
//根据URL判断磁盘缓存中是否存在图片
- (void)diskImageExistsForURL:(nullable NSURL *)url
                   completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock {
    NSString *key = [self cacheKeyForURL:url];

    [self.imageCache diskImageExistsWithKey:key completion:^(BOOL isInDiskCache) {
        // the completion block of checkDiskCacheForImageWithKey:completion: is always called on the main queue, no need to further dispatch
        if (completionBlock) {
            completionBlock(isInDiskCache);
        }
    }];
}
//进行图片下载操作
- (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
    //completedBlock为nil,则触发断言,程序crash
    NSAssert(completedBlock != nil, @"If you mean to prefetch the image, use -[SDWebImagePrefetcher prefetchURLs] instead");

    // Very common mistake is to send the URL using NSString object instead of NSURL. For some strange reason, Xcode won't
    // throw any warning for this type mismatch. Here we failsafe this error by allowing URLs to be passed as NSString.
    if ([url isKindOfClass:NSString.class]) {
        url = [NSURL URLWithString:(NSString *)url];
    }

    // Prevents app crashing on argument type error like sending NSNull instead of NSURL
    if (![url isKindOfClass:NSURL.class]) {
        url = nil;
    }
    //封装下载操作的对象
    __block SDWebImageCombinedOperation *operation = [SDWebImageCombinedOperation new];
    __weak SDWebImageCombinedOperation *weakOperation = operation;

    BOOL isFailedUrl = NO;
    if (url) {
        //为了防止在多线程访问出现问题,创建互斥锁
        @synchronized (self.failedURLs) {
            isFailedUrl = [self.failedURLs containsObject:url];
        }
    }
    //如果url为nil,或者没有设置失败url重新下载的配置且该url已经下载失败过,那么返回失败的回调
    if (url.absoluteString.length == 0 || (!(options & SDWebImageRetryFailed) && isFailedUrl)) {
        [self callCompletionBlockForOperation:operation completion:completedBlock error:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil] url:url];
        return operation;
    }
    //创建互斥锁,添加operation到数组中
    @synchronized (self.runningOperations) {
        [self.runningOperations addObject:operation];
    }
    NSString *key = [self cacheKeyForURL:url];
    //使用缓存对象,根据key去寻找查找
    operation.cacheOperation = [self.imageCache queryCacheOperationForKey:key done:^(UIImage *cachedImage, NSData *cachedData, SDImageCacheType cacheType) {
        //如果当前操作被取消,则remove且return
        if (operation.isCancelled) {
            [self safelyRemoveOperationFromRunning:operation];
            return;
        }
        //(如果没有图片缓存或者设置了重新刷新缓存)且调用代理允许下载图片
        if ((!cachedImage || options & SDWebImageRefreshCached) && (![self.delegate respondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] || [self.delegate imageManager:self shouldDownloadImageForURL:url])) {
            //如果有(其实是缓存的)并且调用者设置了重新刷新缓存,那么先把图片结果回调出去,然后继续去下载图片再更新缓存
            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:weakOperation 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
            //如果缓存没有图片或者请求刷新,并且通过代理下载图片,那么则下载图片
            //下面是根据调用者传进来的option,来匹配设置了哪些,就给downloaderOptions赋值哪些option
            SDWebImageDownloaderOptions downloaderOptions = 0;
            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 &= ~SDWebImageDownloaderProgressiveDownload;
                // ignore image read from NSURLCache if image if cached but force refreshing
                downloaderOptions |= SDWebImageDownloaderIgnoreCachedResponse;
            }
            //在这里真正调用imageDownloader去下载图片了
            SDWebImageDownloadToken *subOperationToken = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage *downloadedImage, NSData *downloadedData, NSError *error, BOOL finished) {
                __strong __typeof(weakOperation) strongOperation = weakOperation;
                //操作取消则不做任何处理
                if (!strongOperation || strongOperation.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:strongOperation completion:completedBlock error:error url:url];

                    if (   error.code != NSURLErrorNotConnectedToInternet
                        && error.code != NSURLErrorCancelled
                        && error.code != NSURLErrorTimedOut
                        && error.code != NSURLErrorInternationalRoamingOff
                        && error.code != NSURLErrorDataNotAllowed
                        && error.code != NSURLErrorCannotFindHost
                        && error.code != NSURLErrorCannotConnectToHost
                        && error.code != NSURLErrorNetworkConnectionLost) {
                        //下载失败则添加图片url到failedURLs集合
                        @synchronized (self.failedURLs) {
                            [self.failedURLs addObject:url];
                        }
                    }
                }
                else {
                    //虽然下载失败,但是如果设置了可以重新下载失败的url则remove该url
                    if ((options & SDWebImageRetryFailed)) {
                        @synchronized (self.failedURLs) {
                            [self.failedURLs removeObject:url];
                        }
                    }
                    //是否需要缓存在磁盘
                    BOOL cacheOnDisk = !(options & SDWebImageCacheMemoryOnly);

                    if (options & SDWebImageRefreshCached && cachedImage && !downloadedImage) {
                        // Image refresh hit the NSURLCache cache, do not call the completion block
                    //图片下载成功并且判断是否需要转换图片
                    } 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];
                                // pass nil if the image was transformed, so we can recalculate the data from the image
                                [self.imageCache storeImage:transformedImage imageData:(imageWasTransformed ? nil : downloadedData) forKey:key toDisk:cacheOnDisk completion:nil];
                            }

                            [self callCompletionBlockForOperation:strongOperation completion:completedBlock image:transformedImage data:downloadedData error:nil cacheType:SDImageCacheTypeNone finished:finished url:url];
                        });
                    } else {
                        //下载完成且有image则缓存图片
                        if (downloadedImage && finished) {
                            [self.imageCache storeImage:downloadedImage imageData:downloadedData forKey:key toDisk:cacheOnDisk completion:nil];
                        }
                        [self callCompletionBlockForOperation:strongOperation completion:completedBlock image:downloadedImage data:downloadedData error:nil cacheType:SDImageCacheTypeNone finished:finished url:url];
                    }
                }
                //如果下载和缓存都完成了则删除操作队列中的operation
                if (finished) {
                    [self safelyRemoveOperationFromRunning:strongOperation];
                }
            }];
            @synchronized(operation) {
                // Need same lock to ensure cancelBlock called because cancel method can be called in different queue
                operation.cancelBlock = ^{
                    [self.imageDownloader cancel:subOperationToken];
                    __strong __typeof(weakOperation) strongOperation = weakOperation;
                    [self safelyRemoveOperationFromRunning:strongOperation];
                };
            }
        } else if (cachedImage) {
            // 有图片且线程没有被取消,则返回有图片的completedBlock
            __strong __typeof(weakOperation) strongOperation = weakOperation;
            [self callCompletionBlockForOperation:strongOperation completion:completedBlock image:cachedImage data:cachedData error:nil cacheType:cacheType finished:YES url:url];
            [self safelyRemoveOperationFromRunning:operation];
        } else {
            // Image not in cache and download disallowed by delegate
            //没有在缓存中并且代理方法也不允许下载则回调失败
            __strong __typeof(weakOperation) strongOperation = weakOperation;
            [self callCompletionBlockForOperation:strongOperation completion:completedBlock image:nil data:nil error:nil cacheType:SDImageCacheTypeNone finished:YES url:url];
            [self safelyRemoveOperationFromRunning:operation];
        }
    }];

    return operation;
}
//将图片存入缓存
- (void)saveImageToCache:(nullable UIImage *)image forURL:(nullable NSURL *)url {
    if (image && url) {
        NSString *key = [self cacheKeyForURL:url];
        [self.imageCache storeImage:image forKey:key toDisk:YES completion:nil];
    }
}
//取消所有的下载操作
- (void)cancelAll {
    @synchronized (self.runningOperations) {
        NSArray<SDWebImageCombinedOperation *> *copiedOperations = [self.runningOperations copy];
        [copiedOperations makeObjectsPerformSelector:@selector(cancel)];
        [self.runningOperations removeObjectsInArray:copiedOperations];
    }
}
//判断当前是否有下载图片
- (BOOL)isRunning {
    BOOL isRunning = NO;
    @synchronized (self.runningOperations) {
        isRunning = (self.runningOperations.count > 0);
    }
    return isRunning;
}
//线程安全的移除下载operation
- (void)safelyRemoveOperationFromRunning:(nullable SDWebImageCombinedOperation*)operation {
    @synchronized (self.runningOperations) {
        if (operation) {
            [self.runningOperations removeObject:operation];
        }
    }
}

- (void)callCompletionBlockForOperation:(nullable SDWebImageCombinedOperation*)operation
                             completion:(nullable SDInternalCompletionBlock)completionBlock
                                  error:(nullable NSError *)error
                                    url:(nullable NSURL *)url {
    [self callCompletionBlockForOperation:operation completion:completionBlock image:nil data:nil error:error cacheType:SDImageCacheTypeNone finished:YES url:url];
}

- (void)callCompletionBlockForOperation:(nullable SDWebImageCombinedOperation*)operation
                             completion:(nullable SDInternalCompletionBlock)completionBlock
                                  image:(nullable UIImage *)image
                                   data:(nullable NSData *)data
                                  error:(nullable NSError *)error
                              cacheType:(SDImageCacheType)cacheType
                               finished:(BOOL)finished
                                    url:(nullable NSURL *)url {
    dispatch_main_async_safe(^{
        if (operation && !operation.isCancelled && completionBlock) {
            completionBlock(image, data, error, cacheType, finished, url);
        }
    });
}

@end


@implementation SDWebImageCombinedOperation
//set方法
- (void)setCancelBlock:(nullable SDWebImageNoParamsBlock)cancelBlock {
    // check if the operation is already cancelled, then we just call the cancelBlock
    if (self.isCancelled) {
        if (cancelBlock) {
            cancelBlock();
        }
        _cancelBlock = nil; // don't forget to nil the cancelBlock, otherwise we will get crashes
    } else {
        _cancelBlock = [cancelBlock copy];
    }
}
//SDWebImageOperation协议方法
- (void)cancel {
    @synchronized(self) {
        self.cancelled = YES;
        if (self.cacheOperation) {
            [self.cacheOperation cancel];
            self.cacheOperation = nil;
        }
        if (self.cancelBlock) {
            self.cancelBlock();
            self.cancelBlock = nil;
        }
    }
}

@end

下边是SDWebImageManager的要紧节点流程图:

3>SDImageCache

SDImageCache是SDWebImage的缓存中央。分三部分构成memory内存缓存,disk硬盘缓存和无缓存组成。
SDImageCache.h文件表明:

typedef NS_ENUM(NSInteger, SDImageCacheType) {
    无缓存类型
    SDImageCacheTypeNone,
    磁盘缓存
    SDImageCacheTypeDisk,
    内存缓存
    SDImageCacheTypeMemory
};
@interface SDImageCache : NSObject
#pragma mark - Properties
//缓存配置对象,包含所有配置项
@property (nonatomic, nonnull, readonly) SDImageCacheConfig *config;
//设置内存容量大小
@property (assign, nonatomic) NSUInteger maxMemoryCost;
//设置内存缓存最大值limit
@property (assign, nonatomic) NSUInteger maxMemoryCountLimit;

#pragma mark - Singleton and initialization
//返回SDImageCache单例
+ (nonnull instancetype)sharedImageCache;
//根据特定的namespace返回一个新的缓存对象
- (nonnull instancetype)initWithNamespace:(nonnull NSString *)ns;
//根据特定的namespace和directory返回一个新的缓存对象
- (nonnull instancetype)initWithNamespace:(nonnull NSString *)ns
                       diskCacheDirectory:(nonnull NSString *)directory NS_DESIGNATED_INITIALIZER;

#pragma mark - Cache paths
//生成缓存路径
- (nullable NSString *)makeDiskCachePath:(nonnull NSString*)fullNamespace;
//添加一个只读的缓存路径
- (void)addReadOnlyCachePath:(nonnull NSString *)path;

#pragma mark - Store Ops
//根据key去异步缓存image,缓存在内存和磁盘
- (void)storeImage:(nullable UIImage *)image
            forKey:(nullable NSString *)key
        completion:(nullable SDWebImageNoParamsBlock)completionBlock;
//根据key去异步缓存image,toDisk未NO不存储在磁盘
- (void)storeImage:(nullable UIImage *)image
            forKey:(nullable NSString *)key
            toDisk:(BOOL)toDisk
        completion:(nullable SDWebImageNoParamsBlock)completionBlock;
//根据key去异步缓存image,toDisk未NO不存储在磁盘 多加一个imageData图片data
- (void)storeImage:(nullable UIImage *)image
         imageData:(nullable NSData *)imageData
            forKey:(nullable NSString *)key
            toDisk:(BOOL)toDisk
        completion:(nullable SDWebImageNoParamsBlock)completionBlock;
//根据key去异步缓存image,缓存在磁盘
- (void)storeImageDataToDisk:(nullable NSData *)imageData forKey:(nullable NSString *)key;

#pragma mark - Query and Retrieve Ops
//异步检查图片是否缓存在磁盘中
- (void)diskImageExistsWithKey:(nullable NSString *)key completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock;
//在缓存中查询对应key的数据
- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key done:(nullable SDCacheQueryCompletedBlock)doneBlock;
//在内存缓存中查询对应key的图片
- (nullable UIImage *)imageFromMemoryCacheForKey:(nullable NSString *)key;
//在磁盘缓存中查询对应key的图片
- (nullable UIImage *)imageFromDiskCacheForKey:(nullable NSString *)key;
//在缓存中查询对应key的图片
- (nullable UIImage *)imageFromCacheForKey:(nullable NSString *)key;

#pragma mark - Remove Ops
//删除缓存中指定key的图片
- (void)removeImageForKey:(nullable NSString *)key withCompletion:(nullable SDWebImageNoParamsBlock)completion;
//删除缓存中指定key的图片 磁盘是可选项
- (void)removeImageForKey:(nullable NSString *)key fromDisk:(BOOL)fromDisk withCompletion:(nullable SDWebImageNoParamsBlock)completion;

#pragma mark - Cache clean Ops
//清空所有的内存缓存
- (void)clearMemory;
//异步清除所有的磁盘缓存
- (void)clearDiskOnCompletion:(nullable SDWebImageNoParamsBlock)completion;
//异步清除所有的失效的缓存图片-因为可以设定缓存时间,超过则失效
- (void)deleteOldFilesWithCompletionBlock:(nullable SDWebImageNoParamsBlock)completionBlock;

#pragma mark - Cache Info
//得到磁盘缓存的大小size
- (NSUInteger)getSize;
//得到在磁盘缓存中图片的数量
- (NSUInteger)getDiskCount;
//异步计算磁盘缓存的大小
- (void)calculateSizeWithCompletionBlock:(nullable SDWebImageCalculateSizeBlock)completionBlock;

#pragma mark - Cache Paths
//获取给定key的缓存路径 需要一个根缓存路径
- (nullable NSString *)cachePathForKey:(nullable NSString *)key inPath:(nonnull NSString *)path;
//获取给定key的默认缓存路径
- (nullable NSString *)defaultCachePathForKey:(nullable NSString *)key;

@end

SDImageCache.m注解:

// See https://github.com/rs/SDWebImage/pull/1141 for discussion
//自定义内存缓存类
@interface AutoPurgeCache : NSCache
@end

@implementation AutoPurgeCache

- (nonnull instancetype)init {
    self = [super init];
    if (self) {
#if SD_UIKIT
        //添加通知,当受到内存警告则移除所有的缓存对象
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(removeAllObjects) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
#endif
    }
    return self;
}

- (void)dealloc {
#if SD_UIKIT
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
#endif
}

@end

//内联函数获得该图片的缓存大小 注意乘以屏幕的比例
FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
#if SD_MAC
    return image.size.height * image.size.width;
#elif SD_UIKIT || SD_WATCH
    return image.size.height * image.size.width * image.scale * image.scale;
#endif
}

@interface SDImageCache ()

#pragma mark - Properties
//内存缓存对象
@property (strong, nonatomic, nonnull) NSCache *memCache;
//磁盘缓存路径
@property (strong, nonatomic, nonnull) NSString *diskCachePath;
//保存缓存路径的数组
@property (strong, nonatomic, nullable) NSMutableArray<NSString *> *customPaths;
//执行处理输入输出的队列
@property (SDDispatchQueueSetterSementics, nonatomic, nullable) dispatch_queue_t ioQueue;

@end


@implementation SDImageCache {
    NSFileManager *_fileManager;
}

#pragma mark - Singleton, init, dealloc
//生成单例SDImageCache
+ (nonnull instancetype)sharedImageCache {
    static dispatch_once_t once;
    static id instance;
    dispatch_once(&once, ^{
        instance = [self new];
    });
    return instance;
}

- (instancetype)init {
    return [self initWithNamespace:@"default"];
}

- (nonnull instancetype)initWithNamespace:(nonnull NSString *)ns {
    NSString *path = [self makeDiskCachePath:ns];
    return [self initWithNamespace:ns diskCacheDirectory:path];
}
//初始化磁盘缓存路径和内存缓存name
- (nonnull instancetype)initWithNamespace:(nonnull NSString *)ns
                       diskCacheDirectory:(nonnull NSString *)directory {
    if ((self = [super init])) {
        NSString *fullNamespace = [@"com.hackemist.SDWebImageCache." stringByAppendingString:ns];

        // Create IO serial queue
        _ioQueue = dispatch_queue_create("com.hackemist.SDWebImageCache", DISPATCH_QUEUE_SERIAL);

        _config = [[SDImageCacheConfig alloc] init];

        // Init the memory cache
        _memCache = [[AutoPurgeCache alloc] init];
        _memCache.name = fullNamespace;

        // Init the disk cache
        if (directory != nil) {
            _diskCachePath = [directory stringByAppendingPathComponent:fullNamespace];
        } else {
            NSString *path = [self makeDiskCachePath:ns];
            _diskCachePath = path;
        }

        dispatch_sync(_ioQueue, ^{
            _fileManager = [NSFileManager new];
        });

#if SD_UIKIT
        // Subscribe to app events
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(clearMemory)
                                                     name:UIApplicationDidReceiveMemoryWarningNotification
                                                   object:nil];

        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(deleteOldFiles)
                                                     name:UIApplicationWillTerminateNotification
                                                   object:nil];

        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(backgroundDeleteOldFiles)
                                                     name:UIApplicationDidEnterBackgroundNotification
                                                   object:nil];
#endif
    }

    return self;
}

- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    SDDispatchQueueRelease(_ioQueue);
}

- (void)checkIfQueueIsIOQueue {
    //dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL)获取当前队列的名字
    //dispatch_queue_get_label获取队列的名字,如果队列没有名字,返回NULL
    const char *currentQueueLabel = dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL);
    const char *ioQueueLabel = dispatch_queue_get_label(self.ioQueue);
    if (strcmp(currentQueueLabel, ioQueueLabel) != 0) {
        NSLog(@"This method should be called from the ioQueue");
    }
}

#pragma mark - Cache paths
////添加一个只读的缓存路径
- (void)addReadOnlyCachePath:(nonnull NSString *)path {
    if (!self.customPaths) {
        self.customPaths = [NSMutableArray new];
    }

    if (![self.customPaths containsObject:path]) {
        [self.customPaths addObject:path];
    }
}
//获取给定key的缓存路径 需要一个根缓存路径
- (nullable NSString *)cachePathForKey:(nullable NSString *)key inPath:(nonnull NSString *)path {
    NSString *filename = [self cachedFileNameForKey:key];
    return [path stringByAppendingPathComponent:filename];
}
//获取给定key的默认缓存路径
- (nullable NSString *)defaultCachePathForKey:(nullable NSString *)key {
    return [self cachePathForKey:key inPath:self.diskCachePath];
}
//根据key值生成文件名:采用MD5
- (nullable NSString *)cachedFileNameForKey:(nullable NSString *)key {
    const char *str = key.UTF8String;
    if (str == NULL) {
        str = "";
    }
    unsigned char r[CC_MD5_DIGEST_LENGTH];
    CC_MD5(str, (CC_LONG)strlen(str), r);
    NSURL *keyURL = [NSURL URLWithString:key];
    NSString *ext = keyURL ? keyURL.pathExtension : key.pathExtension;
    NSString *filename = [NSString stringWithFormat:@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%@",
                          r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], r[8], r[9], r[10],
                          r[11], r[12], r[13], r[14], r[15], ext.length == 0 ? @"" : [NSString stringWithFormat:@".%@", ext]];
    return filename;
}
//生成磁盘路径
- (nullable NSString *)makeDiskCachePath:(nonnull NSString*)fullNamespace {
    NSArray<NSString *> *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
    return [paths[0] stringByAppendingPathComponent:fullNamespace];
}

#pragma mark - Store Ops

- (void)storeImage:(nullable UIImage *)image
            forKey:(nullable NSString *)key
        completion:(nullable SDWebImageNoParamsBlock)completionBlock {
    [self storeImage:image imageData:nil forKey:key toDisk:YES completion:completionBlock];
}

- (void)storeImage:(nullable UIImage *)image
            forKey:(nullable NSString *)key
            toDisk:(BOOL)toDisk
        completion:(nullable SDWebImageNoParamsBlock)completionBlock {
    [self storeImage:image imageData:nil forKey:key toDisk:toDisk completion:completionBlock];
}

- (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) {
        dispatch_async(self.ioQueue, ^{
            @autoreleasepool {
                NSData *data = imageData;
                if (!data && image) {
                    // If we do not have any data to detect image format, use PNG format
                    //如果没有data则采用png的格式进行format
                    data = [[SDWebImageCodersManager sharedInstance] encodedDataWithImage:image format:SDImageFormatPNG];
                }
                [self storeImageDataToDisk:data forKey:key];
            }

            if (completionBlock) {
                dispatch_async(dispatch_get_main_queue(), ^{
                    completionBlock();
                });
            }
        });
    } else {
        if (completionBlock) {
            completionBlock();
        }
    }
}
//利用key进行缓存data
- (void)storeImageDataToDisk:(nullable NSData *)imageData forKey:(nullable NSString *)key {
    if (!imageData || !key) {
        return;
    }

    [self checkIfQueueIsIOQueue];
    //如果文件中不存在磁盘缓存路径 则创建
    if (![_fileManager fileExistsAtPath:_diskCachePath]) {
        [_fileManager createDirectoryAtPath:_diskCachePath withIntermediateDirectories:YES attributes:nil error:NULL];
    }

    // get cache Path for image key  得到该key的缓存路径
    NSString *cachePathForKey = [self defaultCachePathForKey:key];
    // transform to NSUrl  将缓存路径转化为url
    NSURL *fileURL = [NSURL fileURLWithPath:cachePathForKey];
    //将imageData存储起来
    [_fileManager createFileAtPath:cachePathForKey contents:imageData attributes:nil];

    // disable iCloud backup  如果调用者关闭icloud 关闭iCloud备份
    if (self.config.shouldDisableiCloud) {
        [fileURL setResourceValue:@YES forKey:NSURLIsExcludedFromBackupKey error:nil];
    }
}

#pragma mark - Query and Retrieve Ops
//异步检查图片是否缓存在磁盘中
- (void)diskImageExistsWithKey:(nullable NSString *)key completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock {
    dispatch_async(_ioQueue, ^{
        BOOL exists = [_fileManager fileExistsAtPath:[self defaultCachePathForKey:key]];

        // fallback because of https://github.com/rs/SDWebImage/pull/976 that added the extension to the disk file name
        // checking the key with and without the extension
        if (!exists) {
            exists = [_fileManager fileExistsAtPath:[self defaultCachePathForKey:key].stringByDeletingPathExtension];
        }

        if (completionBlock) {
            dispatch_async(dispatch_get_main_queue(), ^{
                completionBlock(exists);
            });
        }
    });
}
//在内存缓存中查询对应key的图片
- (nullable UIImage *)imageFromMemoryCacheForKey:(nullable NSString *)key {
    return [self.memCache objectForKey:key];
}
//在磁盘缓存中查询对应key的图片 并且如果允许内存缓存则在内存中缓存
- (nullable UIImage *)imageFromDiskCacheForKey:(nullable NSString *)key {
    UIImage *diskImage = [self diskImageForKey:key];
    if (diskImage && self.config.shouldCacheImagesInMemory) {
        NSUInteger cost = SDCacheCostForImage(diskImage);
        [self.memCache setObject:diskImage forKey:key cost:cost];
    }

    return diskImage;
}
//在缓存中查询对应key的图片
- (nullable UIImage *)imageFromCacheForKey:(nullable NSString *)key {
    // First check the in-memory cache...
    UIImage *image = [self imageFromMemoryCacheForKey:key];
    if (image) {
        return image;
    }

    // Second check the disk cache...
    image = [self imageFromDiskCacheForKey:key];
    return image;
}
//根据key在磁盘缓存中搜索图片data
- (nullable NSData *)diskImageDataBySearchingAllPathsForKey:(nullable NSString *)key {
    NSString *defaultPath = [self defaultCachePathForKey:key];
    NSData *data = [NSData dataWithContentsOfFile:defaultPath options:self.config.diskCacheReadingOptions error:nil];
    if (data) {
        return data;
    }

    // fallback because of https://github.com/rs/SDWebImage/pull/976 that added the extension to the disk file name
    // checking the key with and without the extension
    data = [NSData dataWithContentsOfFile:defaultPath.stringByDeletingPathExtension options:self.config.diskCacheReadingOptions error:nil];
    if (data) {
        return data;
    }

    NSArray<NSString *> *customPaths = [self.customPaths copy];
    for (NSString *path in customPaths) {
        NSString *filePath = [self cachePathForKey:key inPath:path];
        NSData *imageData = [NSData dataWithContentsOfFile:filePath options:self.config.diskCacheReadingOptions error:nil];
        if (imageData) {
            return imageData;
        }

        // fallback because of https://github.com/rs/SDWebImage/pull/976 that added the extension to the disk file name
        // checking the key with and without the extension
        imageData = [NSData dataWithContentsOfFile:filePath.stringByDeletingPathExtension options:self.config.diskCacheReadingOptions error:nil];
        if (imageData) {
            return imageData;
        }
    }

    return nil;
}
//根据key在磁盘缓存中搜索图片dimage
- (nullable UIImage *)diskImageForKey:(nullable NSString *)key {
    NSData *data = [self diskImageDataBySearchingAllPathsForKey:key];
    if (data) {
        UIImage *image = [[SDWebImageCodersManager sharedInstance] decodedImageWithData:data];
        //根据图片的scale或图片中的图片组 重新计算返回一张新图片
        image = [self scaledImageForKey:key image:image];
        if (self.config.shouldDecompressImages) {
            image = [[SDWebImageCodersManager sharedInstance] decompressedImageWithImage:image data:&data options:@{SDWebImageCoderScaleDownLargeImagesKey: @(NO)}];
        }
        return image;
    } else {
        return nil;
    }
}

- (nullable UIImage *)scaledImageForKey:(nullable NSString *)key image:(nullable UIImage *)image {
    return SDScaledImageForKey(key, image);
}
//在缓存中查询对应key的图片信息 包含image,diskData以及缓存的类型
- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key done:(nullable SDCacheQueryCompletedBlock)doneBlock {
    if (!key) {
        if (doneBlock) {
            doneBlock(nil, nil, SDImageCacheTypeNone);
        }
        return nil;
    }

    // First check the in-memory cache...如果内存缓存包含图片数据则回调结束
    UIImage *image = [self imageFromMemoryCacheForKey:key];
    if (image) {
        NSData *diskData = nil;
        if (image.images) {//imageData都存储在磁盘
            diskData = [self diskImageDataBySearchingAllPathsForKey:key];
        }
        if (doneBlock) {
            doneBlock(image, diskData, SDImageCacheTypeMemory);
        }
        return nil;
    }

    NSOperation *operation = [NSOperation new];
    dispatch_async(self.ioQueue, ^{
        if (operation.isCancelled) {
            // do not call the completion if cancelled
            return;
        }

        @autoreleasepool {
            //到这里说明已经来到磁盘缓存区获取 然后回调结束
            NSData *diskData = [self diskImageDataBySearchingAllPathsForKey:key];
            UIImage *diskImage = [self diskImageForKey:key];
            if (diskImage && self.config.shouldCacheImagesInMemory) {
                NSUInteger cost = SDCacheCostForImage(diskImage);
                [self.memCache setObject:diskImage forKey:key cost:cost];
            }

            if (doneBlock) {
                dispatch_async(dispatch_get_main_queue(), ^{
                    doneBlock(diskImage, diskData, SDImageCacheTypeDisk);
                });
            }
        }
    });

    return operation;
}

#pragma mark - Remove Ops
//删除缓存中指定key的图片
- (void)removeImageForKey:(nullable NSString *)key withCompletion:(nullable SDWebImageNoParamsBlock)completion {
    [self removeImageForKey:key fromDisk:YES withCompletion:completion];
}
//删除缓存中指定key的图片 磁盘是可选项
- (void)removeImageForKey:(nullable NSString *)key fromDisk:(BOOL)fromDisk withCompletion:(nullable SDWebImageNoParamsBlock)completion {
    if (key == nil) {
        return;
    }
    //如果内存也缓存了,则删除内存缓存
    if (self.config.shouldCacheImagesInMemory) {
        [self.memCache removeObjectForKey:key];
    }

    if (fromDisk) {
        dispatch_async(self.ioQueue, ^{
            [_fileManager removeItemAtPath:[self defaultCachePathForKey:key] error:nil];

            if (completion) {
                dispatch_async(dispatch_get_main_queue(), ^{
                    completion();
                });
            }
        });
    } else if (completion){
        completion();
    }

}

# pragma mark - Mem Cache settings

- (void)setMaxMemoryCost:(NSUInteger)maxMemoryCost {
    self.memCache.totalCostLimit = maxMemoryCost;
}

- (NSUInteger)maxMemoryCost {
    return self.memCache.totalCostLimit;
}

- (NSUInteger)maxMemoryCountLimit {
    return self.memCache.countLimit;
}

- (void)setMaxMemoryCountLimit:(NSUInteger)maxCountLimit {
    self.memCache.countLimit = maxCountLimit;
}

#pragma mark - Cache clean Ops
//清空所有的内存缓存
- (void)clearMemory {
    [self.memCache removeAllObjects];
}
//异步清除所有的磁盘缓存
- (void)clearDiskOnCompletion:(nullable SDWebImageNoParamsBlock)completion {
    dispatch_async(self.ioQueue, ^{
        [_fileManager removeItemAtPath:self.diskCachePath error:nil];
        [_fileManager createDirectoryAtPath:self.diskCachePath
                withIntermediateDirectories:YES
                                 attributes:nil
                                      error:NULL];

        if (completion) {
            dispatch_async(dispatch_get_main_queue(), ^{
                completion();
            });
        }
    });
}

- (void)deleteOldFiles {
    [self deleteOldFilesWithCompletionBlock:nil];
}
//异步清除所有失效的缓存图片-因为可以设定缓存时间,超过则失效
- (void)deleteOldFilesWithCompletionBlock:(nullable SDWebImageNoParamsBlock)completionBlock {
    dispatch_async(self.ioQueue, ^{
        NSURL *diskCacheURL = [NSURL fileURLWithPath:self.diskCachePath isDirectory:YES];
        //resourceKeys数组包含遍历文件的属性,NSURLIsDirectoryKey判断遍历到的URL所指对象是否是目录,
        //NSURLContentModificationDateKey判断遍历返回的URL所指项目的最后修改时间,NSURLTotalFileAllocatedSizeKey判断URL目录中所分配的空间大小
        NSArray<NSString *> *resourceKeys = @[NSURLIsDirectoryKey, NSURLContentModificationDateKey, NSURLTotalFileAllocatedSizeKey];

        // This enumerator prefetches useful properties for our cache files.
        //利用目录枚举器遍历指定磁盘缓存路径目录下的文件,从而我们活的文件大小,缓存时间等信息
        NSDirectoryEnumerator *fileEnumerator = [_fileManager enumeratorAtURL:diskCacheURL
                                                   includingPropertiesForKeys:resourceKeys
                                                                      options:NSDirectoryEnumerationSkipsHiddenFiles
                                                                 errorHandler:NULL];
        //计算过期时间,默认1周以前的缓存文件是过期失效
        NSDate *expirationDate = [NSDate dateWithTimeIntervalSinceNow:-self.config.maxCacheAge];
        //保存遍历的文件url
        NSMutableDictionary<NSURL *, NSDictionary<NSString *, id> *> *cacheFiles = [NSMutableDictionary dictionary];
        //保存当前缓存的大小
        NSUInteger currentCacheSize = 0;

        // Enumerate all of the files in the cache directory.  This loop has two purposes:
        //
        //  1. Removing files that are older than the expiration date.
        //  2. Storing file attributes for the size-based cleanup pass.
        //保存删除的文件url
        NSMutableArray<NSURL *> *urlsToDelete = [[NSMutableArray alloc] init];
        //遍历目录枚举器,目的1删除过期文件 2纪录文件大小,以便于之后删除使用
        for (NSURL *fileURL in fileEnumerator) {
            NSError *error;
            //获取指定url对应文件的指定三种属性的key和value
            NSDictionary<NSString *, id> *resourceValues = [fileURL resourceValuesForKeys:resourceKeys error:&error];

            // Skip directories and errors.
            //如果是文件夹则返回
            if (error || !resourceValues || [resourceValues[NSURLIsDirectoryKey] boolValue]) {
                continue;
            }

            // Remove files that are older than the expiration date;
            //获取指定url文件对应的修改日期
            NSDate *modificationDate = resourceValues[NSURLContentModificationDateKey];
            //如果修改日期大于指定日期,则加入要移除的数组里
            if ([[modificationDate laterDate:expirationDate] isEqualToDate:expirationDate]) {
                [urlsToDelete addObject:fileURL];
                continue;
            }

            // Store a reference to this file and account for its total size.
            //获取指定的url对应的文件的大小,并且把url与对应大小存入一个字典中
            NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey];
            currentCacheSize += totalAllocatedSize.unsignedIntegerValue;
            cacheFiles[fileURL] = resourceValues;
        }
        //删除所有最后修改日期大于指定日期的所有文件
        for (NSURL *fileURL in urlsToDelete) {
            [_fileManager removeItemAtURL:fileURL error:nil];
        }

        // If our remaining disk cache exceeds a configured maximum size, perform a second
        // size-based cleanup pass.  We delete the oldest files first.
        //如果当前缓存的大小超过了默认大小,则按照日期删除,直到缓存大小<默认大小的一半
        if (self.config.maxCacheSize > 0 && currentCacheSize > self.config.maxCacheSize) {
            // Target half of our maximum cache size for this cleanup pass.
            const NSUInteger desiredCacheSize = self.config.maxCacheSize / 2;

            // Sort the remaining cache files by their last modification time (oldest first).
            //根据文件创建的时间排序
            NSArray<NSURL *> *sortedFiles = [cacheFiles keysSortedByValueWithOptions:NSSortConcurrent
                                                                     usingComparator:^NSComparisonResult(id obj1, id obj2) {
                                                                         return [obj1[NSURLContentModificationDateKey] compare:obj2[NSURLContentModificationDateKey]];
                                                                     }];

            // Delete files until we fall below our desired cache size.
            //迭代删除缓存,直到缓存大小是默认缓存大小的一半
            for (NSURL *fileURL in sortedFiles) {
                if ([_fileManager removeItemAtURL:fileURL error:nil]) {
                    NSDictionary<NSString *, id> *resourceValues = cacheFiles[fileURL];
                    NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey];
                    currentCacheSize -= totalAllocatedSize.unsignedIntegerValue;

                    if (currentCacheSize < desiredCacheSize) {
                        break;
                    }
                }
            }
        }
        //在主线程中回调
        if (completionBlock) {
            dispatch_async(dispatch_get_main_queue(), ^{
                completionBlock();
            });
        }
    });
}

#if SD_UIKIT
//应用进入后台的时候,调用这个方法 然后清除过期图片
- (void)backgroundDeleteOldFiles {
    Class UIApplicationClass = NSClassFromString(@"UIApplication");
    if(!UIApplicationClass || ![UIApplicationClass respondsToSelector:@selector(sharedApplication)]) {
        return;
    }
    UIApplication *application = [UIApplication performSelector:@selector(sharedApplication)];
    __block UIBackgroundTaskIdentifier bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
        // Clean up any unfinished task business by marking where you
        // stopped or ending the task outright.
        [application endBackgroundTask:bgTask];
        bgTask = UIBackgroundTaskInvalid;
    }];

    // Start the long-running task and return immediately.
    [self deleteOldFilesWithCompletionBlock:^{
        [application endBackgroundTask:bgTask];
        bgTask = UIBackgroundTaskInvalid;
    }];
}
#endif

#pragma mark - Cache Info
//得到磁盘缓存的大小size
- (NSUInteger)getSize {
    __block NSUInteger size = 0;
    dispatch_sync(self.ioQueue, ^{
        NSDirectoryEnumerator *fileEnumerator = [_fileManager enumeratorAtPath:self.diskCachePath];
        for (NSString *fileName in fileEnumerator) {
            NSString *filePath = [self.diskCachePath stringByAppendingPathComponent:fileName];
            NSDictionary<NSString *, id> *attrs = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil];
            size += [attrs fileSize];
        }
    });
    return size;
}
//得到在磁盘缓存中图片的数量
- (NSUInteger)getDiskCount {
    __block NSUInteger count = 0;
    dispatch_sync(self.ioQueue, ^{
        NSDirectoryEnumerator *fileEnumerator = [_fileManager enumeratorAtPath:self.diskCachePath];
        count = fileEnumerator.allObjects.count;
    });
    return count;
}
//异步计算磁盘缓存的大小
- (void)calculateSizeWithCompletionBlock:(nullable SDWebImageCalculateSizeBlock)completionBlock {
    NSURL *diskCacheURL = [NSURL fileURLWithPath:self.diskCachePath isDirectory:YES];

    dispatch_async(self.ioQueue, ^{
        NSUInteger fileCount = 0;
        NSUInteger totalSize = 0;

        NSDirectoryEnumerator *fileEnumerator = [_fileManager enumeratorAtURL:diskCacheURL
                                                   includingPropertiesForKeys:@[NSFileSize]
                                                                      options:NSDirectoryEnumerationSkipsHiddenFiles
                                                                 errorHandler:NULL];

        for (NSURL *fileURL in fileEnumerator) {
            NSNumber *fileSize;
            [fileURL getResourceValue:&fileSize forKey:NSURLFileSizeKey error:NULL];
            totalSize += fileSize.unsignedIntegerValue;
            fileCount += 1;
        }

        if (completionBlock) {
            dispatch_async(dispatch_get_main_queue(), ^{
                completionBlock(fileCount, totalSize);
            });
        }
    });
}

@end

下图是在SDWebImageManager中调用SDCacheImage查找缓存的重大流程图:

此篇小说讲解的是UIImageView+WebCache/UIView+WebCache,SDWebImageManager和SDImageCache多少个举足轻重类,有许多很值得大家去读书的点,例如:
1.善用接口分离原则-设计更好的对外调用API。
2.适当做相当处理体制-这一个至极处理可以避免消耗不必要的资源仍然异常产生。例如SDWebImageManager中

 if (!url) { return @"";}
 if ([url isKindOfClass:NSString.class]) {
     url = [NSURL URLWithString:(NSString *)url];
 }

3.充实互斥锁-起到线程的保障功效。

@synchronized (self.failedURLs) {
     isFailedUrl = [self.failedURLs containsObject:url];
 }

4.多采用inline函数-提升程序的施行效用。

FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
#if SD_MAC
    return image.size.height * image.size.width;
#elif SD_UIKIT || SD_WATCH
    return image.size.height * image.size.width * image.scale * image.scale;
#endif
}

5.巧妙运用封装思想和分支概念,写出越来越平台化的零部件-例如我们平常行使SD大多都是运用UIImageView+WebCache的API,
其实SDWebImageManager是一点一滴可以抽出来单独使用,不会因为跳过了UIImageView+WebCache没了依赖而一筹莫展运用。假使项目是索要几人跨多部门通力合作,假如别人或其他机关需要调用你写的一个很牛逼的意义,这这么些平台化就很关键了。
SDWebImage还有为数不少值得我们借鉴和学习的地方,需要我们细细研读。下一篇会讲解SDWebImageDownloader和SDWebImageDownloaderOperation。