源码阅读:SDWebImage(十四)——SDWebImageManager

1,212 阅读15分钟

该文章阅读的SDWebImage的版本为4.3.3。

这个类是加载图像的核心类,它处理了图像缓存,图像下载功能的逻辑,为外部提供了简洁的图像加载方法。

1.公共枚举

/**
 这个位枚举定义了图像加载的可选项
 */
typedef NS_OPTIONS(NSUInteger, SDWebImageOptions) {
    /**
     默认情况下,当url无法下载时,url会被列入黑名单,之后再加载该url就不会继续尝试。
     设置这个选项可禁用此黑名单,可以再次下载已经下载失败过的url
     */
    SDWebImageRetryFailed = 1 << 0,

    /**
     默认情况下,在UI交互期间图像下载就开始了。
     设置这个选项可禁用此功能,比如在UIScrollView滚动变慢时就会延迟图像下载。
     */
    SDWebImageLowPriority = 1 << 1,

    /**
     默认情况下,图像下载完成后,会被缓存到内存和硬盘中。
     设置这个选项可禁用硬盘缓存,仅缓存在内存中。
     */
    SDWebImageCacheMemoryOnly = 1 << 2,

    /**
     默认情况下,图像仅在完全下载后显示。
     设置这个选项可启用渐进式下载,图像在浏览器中下载时逐步显示。
     */
    SDWebImageProgressiveDownload = 1 << 3,

    /**
     默认情况下,同一个url不会重复下载,下载成功后再次加载该url会直接从缓存中获取图像。
     但有一种情况是,该url对应的服务器资源发生了变化,但是该url没有变。
     设置这个选项就可以重新下载该url并刷新对应的缓存。
     */
    SDWebImageRefreshCached = 1 << 4,

    /**
     默认情况下,应用切到后台后就停止下载。
     设置这个选项可向系统申请额外的时间来实现后台下载。
     */
    SDWebImageContinueInBackground = 1 << 5,

    /**
     是否使用默认的cookie处理请求,设置这个选项就为是
     */
    SDWebImageHandleCookies = 1 << 6,

    /**
     是否允许通过不受信任的SSL证书,,设置这个选项就为是
     */
    SDWebImageAllowInvalidSSLCertificates = 1 << 7,

    /**
     默认情况下,图像是按顺序下载。
     设置这个选项可使该图像下载拥有高优先级,以排在较前的顺序下载
     */
    SDWebImageHighPriority = 1 << 8,
    
    /**
     默认情况下,在加载图像时会先加载占位图。 
     设置这个选项可延迟加载占位图,直到图像加载完成。
     */
    SDWebImageDelayPlaceholder = 1 << 9,

    /**
     默认情况下,处理动图时不会调用imageManager:transformDownloadedImage:withURL:这个代理方法。
     设置这个选项可在处理动图时也调用该代理方法。
     */
    SDWebImageTransformAnimatedImage = 1 << 10,
    
    /**
     默认情况下,图像下载完成后会被自动添加到imageView上。
     但有一种情况是,我们想在图像被添加到imageView上之前对图像做一些处理。
     设置这个选项可使图像不自动添加到imageView,而手动添加。
     */
    SDWebImageAvoidAutoSetImage = 1 << 11,
    
    /**
     默认情况下,图像会根据其原始大小进行解码。
     设置这个选项可压缩图像大小。
     但是如果也同时设置了SDWebImageProgressiveDownload这个选项,压缩图像的功能就会失效。
     */
    SDWebImageScaleDownLargeImages = 1 << 12,
    
    /**
     默认情况下,当内存中有缓存时,就不会再去查询硬盘中的缓存。
     设置这个选项可强制同时查询硬盘中的缓存。
     但是建议和SDWebImageQueryDiskSync一起使用,以确保图像在同一个runloop中加载。
     */
    SDWebImageQueryDataWhenInMemory = 1 << 13,
    
    /**
     默认情况下,是同步查询内存缓存,异步查询硬盘缓存。 设置这个选项可强制同步查询硬盘缓存,以确保在同一个runloop中加载图像。
     */
    SDWebImageQueryDiskSync = 1 << 14,
    
    /**
     默认情况下,当没有缓存时,才从网络下载图像。
     设置这个选项可强制从网络下载图像。
     */
    SDWebImageFromCacheOnly = 1 << 15,
    /**
     默认情况下,使用SDWebImageTransition添加的过渡动画只适用于从网络下载的图像。
     设置这个选项可使过渡动画也强制适用于从缓存获取的图像
     */
    SDWebImageForceTransition = 1 << 16
};

2.公共类型定义

/**
 用于外部分类中的完成回调block
 */
typedef void(^SDExternalCompletionBlock)(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL);
/**
 用于该类内部中的完成回调block
 */
typedef void(^SDInternalCompletionBlock)(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL);
/**
 用于将url处理成缓存用的key的方法,可以删除url中的查询字段
 */
typedef NSString * _Nullable(^SDWebImageCacheKeyFilterBlock)(NSURL * _Nullable url);
/**
 用于将图像缓存到硬盘的解码算法
 */
typedef NSData * _Nullable(^SDWebImageCacheSerializerBlock)(UIImage * _Nonnull image, NSData * _Nullable data, NSURL * _Nullable imageURL);

3.SDWebImageManagerDelegate代理

/**
 如果没找到url对应的缓存,url需要从网络下载时会调用这个代理方法
 */
- (BOOL)imageManager:(nonnull SDWebImageManager *)imageManager shouldDownloadImageForURL:(nullable NSURL *)imageURL;
/**
 如果url对应的图像下载失败了,就会调用这个代理方法
 */
- (BOOL)imageManager:(nonnull SDWebImageManager *)imageManager shouldBlockFailedURL:(nonnull NSURL *)imageURL withError:(nonnull NSError *)error;
/**
 图像下载完成以后,如果想转换图片就调用这个代理方法
 */
- (nullable UIImage *)imageManager:(nonnull SDWebImageManager *)imageManager transformDownloadedImage:(nullable UIImage *)image withURL:(nullable NSURL *)imageURL;

4.公共属性

/**
 代理
 */
@property (weak, nonatomic, nullable) id <SDWebImageManagerDelegate> delegate;
/**
 图像缓存对象
 */
@property (strong, nonatomic, readonly, nullable) SDImageCache *imageCache;
/**
 图像下载器
 */
@property (strong, nonatomic, readonly, nullable) SDWebImageDownloader *imageDownloader;
/**
 将url处理成key的方法
 */
@property (nonatomic, copy, nullable) SDWebImageCacheKeyFilterBlock cacheKeyFilter;
/**
 图像解码的方法
 */
@property (nonatomic, copy, nullable) SDWebImageCacheSerializerBlock cacheSerializer;

5.公共方法

/**
 获取单例对象的方法
 */
+ (nonnull instancetype)sharedManager;
/**
 以指定图像缓存对象和图像下载器初始化对象
 */
- (nonnull instancetype)initWithCache:(nonnull SDImageCache *)cache downloader:(nonnull SDWebImageDownloader *)downloader NS_DESIGNATED_INITIALIZER;
/**
 加载url图像的方法,如果没有缓存就从网络下载
 */
- (nullable id <SDWebImageOperation>)loadImageWithURL:(nullable NSURL *)url
                                              options:(SDWebImageOptions)options
                                             progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                                            completed:(nullable SDInternalCompletionBlock)completedBlock;
/**
 以指定的url缓存指定图像
 */
- (void)saveImageToCache:(nullable UIImage *)image forURL:(nullable NSURL *)url;
/**
 取消当前所有操作
 */
- (void)cancelAll;
/**
 查看当前是否有操作正在进行
 */
- (BOOL)isRunning;
/**
 异步查询指定url是否有缓存
 */
- (void)cachedImageExistsForURL:(nullable NSURL *)url
                     completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock;
/**
 异步查询指定url在硬盘中是否有缓存
 */
- (void)diskImageExistsForURL:(nullable NSURL *)url
                   completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock;
/**
 获取指定url的缓存key
 */
- (nullable NSString *)cacheKeyForURL:(nullable NSURL *)url;

6.SDWebImageCombinedOperation类

6.1.属性

/**
 取消
 */
@property (assign, nonatomic, getter = isCancelled) BOOL cancelled;
/**
 图像下载token
 */
@property (strong, nonatomic, nullable) SDWebImageDownloadToken *downloadToken;
/**
 缓存操作对象
 */
@property (strong, nonatomic, nullable) NSOperation *cacheOperation;
/**
 图像管理者对象
 */
@property (weak, nonatomic, nullable) SDWebImageManager *manager;

6.2.方法

- (void)cancel {
    // 加锁
    @synchronized(self) {
        // 记录取消状态
        self.cancelled = YES;
        // 如果正在缓存就取消缓存,并将属性置空
        if (self.cacheOperation) {
            [self.cacheOperation cancel];
            self.cacheOperation = nil;
        }
        // 如果正在下载就取消下载
        if (self.downloadToken) {
            [self.manager.imageDownloader cancel:self.downloadToken];
        }
        // 移除掉当前操作
        [self.manager safelyRemoveOperationFromRunning:self];
    }
}

7.类扩展属性

/**
 保存图像缓存对象
 */
@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;

8.实现

8.1.生命周期方法实现

- (nonnull instancetype)init {
    // 创建图像缓存对象
    SDImageCache *cache = [SDImageCache sharedImageCache];
    // 创建图像下载对象
    SDWebImageDownloader *downloader = [SDWebImageDownloader sharedDownloader];
    // 用万能方法初始化对象并返回
    return [self initWithCache:cache downloader:downloader];
}

8.2.私有方法实现

/**
 缩放图像对象
 */
- (nullable UIImage *)scaledImageForKey:(nullable NSString *)key image:(nullable UIImage *)image {
    return SDScaledImageForKey(key, image);
}
- (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(^{
        // 如果有图像加载操作封装对象,
        // 并且图像加载操作封装对象没被取消
        // 并且有完成回调block
        if (operation && !operation.isCancelled && completionBlock) {
            // 回调结果
            completionBlock(image, data, error, cacheType, finished, url);
        }
    });
}

8.3.公共方法实现

+ (nonnull instancetype)sharedManager {
    // 获取单例对象
    static dispatch_once_t once;
    static id instance;
    dispatch_once(&once, ^{
        instance = [self new];
    });
    return instance;
}
- (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;
}
- (nullable NSString *)cacheKeyForURL:(nullable NSURL *)url {
    // 如果没传url就返回空字符串
    if (!url) {
        return @"";
    }

    // 如果设置了url处理方法就调用处理方法,否则就返回url的字符串格式
    if (self.cacheKeyFilter) {
        return self.cacheKeyFilter(url);
    } else {
        return url.absoluteString;
    }
}
- (void)cachedImageExistsForURL:(nullable NSURL *)url
                     completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock {
    // 获取url对应的key
    NSString *key = [self cacheKeyForURL:url];
    
    // 通过缓存对象获取key对应的内存缓存
    BOOL isInMemoryCache = ([self.imageCache imageFromMemoryCacheForKey:key] != nil);
    
    // 如果有内存缓存
    if (isInMemoryCache) {
        // 主队列异步回调
        dispatch_async(dispatch_get_main_queue(), ^{
            if (completionBlock) {
                completionBlock(YES);
            }
        });
        return;
    }
    
    // 通过缓存对象获取key对应的硬盘缓存
    [self.imageCache diskImageExistsWithKey:key completion:^(BOOL isInDiskCache) {
        // 回调
        if (completionBlock) {
            completionBlock(isInDiskCache);
        }
    }];
}
- (void)diskImageExistsForURL:(nullable NSURL *)url
                   completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock {
    // 获取url对应的key
    NSString *key = [self cacheKeyForURL:url];
    
    // 通过缓存对象获取key对应的硬盘缓存
    [self.imageCache diskImageExistsWithKey:key completion:^(BOOL isInDiskCache) {
        // 回调
        if (completionBlock) {
            completionBlock(isInDiskCache);
        }
    }];
}
- (id <SDWebImageOperation>)loadImageWithURL:(nullable NSURL *)url
                                     options:(SDWebImageOptions)options
                                    progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                                   completed:(nullable SDInternalCompletionBlock)completedBlock {
    // 按成回调block是必传的
    NSAssert(completedBlock != nil, @"If you mean to prefetch the image, use -[SDWebImagePrefetcher prefetchURLs] instead");

    // 虽然参数要求传NSURL类型对象但是如果传NSStriing类型对象并不会有警告,所以再做一下处理
    if ([url isKindOfClass:NSString.class]) {
        url = [NSURL URLWithString:(NSString *)url];
    }

    // 防止参数url的类型错误导致崩溃,例如url的是NSNull类型的
    if (![url isKindOfClass:NSURL.class]) {
        url = nil;
    }

    // 生成一个图像加载操作的封装对象
    SDWebImageCombinedOperation *operation = [SDWebImageCombinedOperation new];
    operation.manager = self;
    
    // 定义变量保存要加载的url是否失败过
    BOOL isFailedUrl = NO;
    if (url) {
        // 加锁
        @synchronized (self.failedURLs) {
            // 判断是否失败过
            isFailedUrl = [self.failedURLs containsObject: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;
    }

    // 加锁
    @synchronized (self.runningOperations) {
        // 保存当前操作封装对象到属性中
        [self.runningOperations addObject:operation];
    }
    // 获取url对应的key
    NSString *key = [self cacheKeyForURL:url];
    
    // 创建变量保存图像缓存选项
    SDImageCacheOptions cacheOptions = 0;
    // 根据设置的选项设置图像缓存的选项
    if (options & SDWebImageQueryDataWhenInMemory) cacheOptions |= SDImageCacheQueryDataWhenInMemory;
    if (options & SDWebImageQueryDiskSync) cacheOptions |= SDImageCacheQueryDiskSync;
    
    // 通过缓存对象查询url对应的缓存
    __weak SDWebImageCombinedOperation *weakOperation = operation;
    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;
        }
        
        // 创建变量保存是否要下载图像
        // 想要从网络下载图像必须同时满足以下条件:
        // 没有设置只从缓存加载的选项
        // 没找到缓存图像,或者设置了需要刷新缓存图像的选项
        // 代理对象没有实现这个代理方法,或者代理对象实现了这个代理方法并且代理方法返回了YES,意思是在缓存中找不到图片时要从网络上下载
        BOOL shouldDownload = (!(options & SDWebImageFromCacheOnly))
            && (!cachedImage || options & SDWebImageRefreshCached)
            && (![self.delegate respondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] || [self.delegate imageManager:self shouldDownloadImageForURL:url]);
        // 如果需要下载图像
        if (shouldDownload) {
            // 如果有图像缓存并且设置了刷新图片缓存,就先调用完成回调block
            if (cachedImage && options & SDWebImageRefreshCached) {
                [self callCompletionBlockForOperation:strongOperation completion:completedBlock image:cachedImage data:cachedData error:nil cacheType:cacheType finished:YES url:url];
            }

            // 创建变量保存图像下载的选项
            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) {
                // 取消渐进下载
                downloaderOptions &= ~SDWebImageDownloaderProgressiveDownload;
                // 忽视从NSURLCache中获取缓存
                downloaderOptions |= SDWebImageDownloaderIgnoreCachedResponse;
            }
            
            // 开启下载任务并获取下载token
            __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;
                if (!strongSubOperation || strongSubOperation.isCancelled) {
                     // 如果图像加载操作封装对象不存在,
                     // 或者图像加载操作封装对象被取消,
                     // 就什么都不做
                } else if (error) {
                    // 如果下载出错
                    // 通过完成回调block回调错误
                    [self callCompletionBlockForOperation:strongSubOperation completion:completedBlock error:error url:url];
                    // 创建变量保存是否保存错误url
                    BOOL shouldBlockFailedURL;
                    if ([self.delegate respondsToSelector:@selector(imageManager:shouldBlockFailedURL:withError:)]) {
                        // 如果实现了代理就遵循代理方法返回的数据
                        shouldBlockFailedURL = [self.delegate imageManager:self shouldBlockFailedURL:url withError:error];
                    } else {
                        // 如果没有实现代理,
                        // 想要阻止下载失败的链接就得满足以下条件:
                        // 不是没联网、
                        // 不是被取消、
                        // 不是连接超时、
                        // 不是关闭了国际漫游、
                        // 不是不允许蜂窝数据连接、
                        // 不是没有找到host、
                        // 不是无法连接host、
                        // 不是连接丢失
                        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);
                    }
                    
                    // 如果需要阻止失败url再次下载,
                    // 就把url添加到黑名单中保存
                    if (shouldBlockFailedURL) {
                        @synchronized (self.failedURLs) {
                            [self.failedURLs addObject:url];
                        }
                    }
                }
                else {
                    // 如果下载成功
                    // 如果设置了重新下载失败的url选项,
                    // 就把url从黑名单中移除
                    if ((options & SDWebImageRetryFailed)) {
                        @synchronized (self.failedURLs) {
                            [self.failedURLs removeObject:url];
                        }
                    }
                    
                    // 创建变量保存是否缓存到硬盘,并根据设置的选项赋值
                    BOOL cacheOnDisk = !(options & SDWebImageCacheMemoryOnly);
                    
                    // 在单例管理对象SDWebImageDownloader中已经实现了图片的缩放,这里是用于自定义管理对象以避免额外的缩放
                    if (self != [SDWebImageManager sharedManager] && self.cacheKeyFilter && downloadedImage) {
                        // 如果当前对象不是SDWebImageManager的单例对象,
                        // 并且设置里过滤链接代码块,
                        // 并且下载到了图片。就进行缩放
                        downloadedImage = [self scaledImageForKey:key image:downloadedImage];
                    }

                    if (options & SDWebImageRefreshCached && cachedImage && !downloadedImage) {
                        // 如果设置了刷新缓存选项,
                        // 并且有缓存图,
                        // 并且没有下载图,
                        // 就什么也不需要做
                    } 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);
                                }
                                // 通过缓存对象缓存图像
                                [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];
                            }
                        }
                        // 调用完成回调block回调结果
                        [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) {
             // 如果不需要下载,
             // 并且获取到了缓存图,
             // 调用完成回调block回调结果
            [self callCompletionBlockForOperation:strongOperation completion:completedBlock image:cachedImage data:cachedData error:nil cacheType:cacheType finished:YES url:url];
            // 把当前图像加载操作封装对象从集合属性中移除
            [self safelyRemoveOperationFromRunning:strongOperation];
        } else {
            // 如果不需要下载,
            // 并且也没有缓存图
            // 调用完成回调block回调结果
            [self callCompletionBlockForOperation:strongOperation completion:completedBlock image:nil data:nil error:nil cacheType:SDImageCacheTypeNone finished:YES url:url];
            // 把当前图像加载操作封装对象从集合属性中移除
            [self safelyRemoveOperationFromRunning:strongOperation];
        }
    }];
    
    // 返回图像加载操作封装对象
    return operation;
}
- (void)saveImageToCache:(nullable UIImage *)image forURL:(nullable NSURL *)url {
    // 必须要传参数
    if (image && url) {
        // 获取url对应的key
        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 {
    // 创建变量保存执行状态,默认为NO
    BOOL isRunning = NO;
    // 加锁
    @synchronized (self.runningOperations) {
        // 如果当前有正在执行的操作就为YES
        isRunning = (self.runningOperations.count > 0);
    }
    // 返回状态
    return isRunning;
}

9.总结

可以看到,由于有良好的框架设计,在实现图像加载操作时的逻辑就很清晰: 由图像缓存对象SDImageCache来负责缓存的查找和添加;由图像下载对象SDWebImageDownloader来负责图像的网络下载;由图像加载操作封装对象SDWebImageCombinedOperation来负责整体图像加载操作的控制。所以,一个良好的架构是非常非常必要和重要的。

源码阅读系列:SDWebImage

源码阅读:SDWebImage(一)——从使用入手

源码阅读:SDWebImage(二)——SDWebImageCompat

源码阅读:SDWebImage(三)——NSData+ImageContentType

源码阅读:SDWebImage(四)——SDWebImageCoder

源码阅读:SDWebImage(五)——SDWebImageFrame

源码阅读:SDWebImage(六)——SDWebImageCoderHelper

源码阅读:SDWebImage(七)——SDWebImageImageIOCoder

源码阅读:SDWebImage(八)——SDWebImageGIFCoder

源码阅读:SDWebImage(九)——SDWebImageCodersManager

源码阅读:SDWebImage(十)——SDImageCacheConfig

源码阅读:SDWebImage(十一)——SDImageCache

源码阅读:SDWebImage(十二)——SDWebImageDownloaderOperation

源码阅读:SDWebImage(十三)——SDWebImageDownloader

源码阅读:SDWebImage(十四)——SDWebImageManager

源码阅读:SDWebImage(十五)——SDWebImagePrefetcher

源码阅读:SDWebImage(十六)——SDWebImageTransition

源码阅读:SDWebImage(十七)——UIView+WebCacheOperation

源码阅读:SDWebImage(十八)——UIView+WebCache

源码阅读:SDWebImage(十九)——UIImage+ForceDecode/UIImage+GIF/UIImage+MultiFormat

源码阅读:SDWebImage(二十)——UIButton+WebCache

源码阅读:SDWebImage(二十一)——UIImageView+WebCache/UIImageView+HighlightedWebCache