SDWebImage源码解读(设置图片流程)

2019-04-08  本文已影响0人  尤先森

建议:在开始看之前,先打开一份SDWebImage源码,一步一步跟着来

SDWebImage使用姿势

一般情况我们都是这么用的对吧,那就用这个方法作为入口,一步一步深入。
切记:我们解读按照sd_setImageWithURL来操作,所以,大多数情况,统一入口传入的很多参数大多为nil。

#import "UIImageView+WebCache.h"

[self.imageView sd_setImageWithURL:url];

源码解读

1.API
//纯粹通过url获取图片
- (void)sd_setImageWithURL:(nullable NSURL *)url {
    [self sd_setImageWithURL:url placeholderImage:nil options:0 progress:nil completed:nil];
}

//通过url 获取图片,在图片加载出来前,你可以先设置一个placeholder image 类似预览图
- (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder {
    [self sd_setImageWithURL:url placeholderImage:placeholder options:0 progress:nil completed:nil];
}
//通过url 获取图片,设置placeholder的同时,可以添加SDWebImageOptions选项
- (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options {
    [self sd_setImageWithURL:url placeholderImage:placeholder options:options progress:nil completed:nil];
}
//通过url 获取图片后,会有一个block回调
- (void)sd_setImageWithURL:(nullable NSURL *)url completed:(nullable SDExternalCompletionBlock)completedBlock {
    [self sd_setImageWithURL:url placeholderImage:nil options:0 progress:nil completed:completedBlock];
}
//通过url 获取图片 有placeholder 也有block回调
- (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder completed:(nullable SDExternalCompletionBlock)completedBlock {
    [self sd_setImageWithURL:url placeholderImage:placeholder options:0 progress:nil completed:completedBlock];
}
//通过url 获取图片 有placeholder 也有block回调 还有options
- (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options completed:(nullable SDExternalCompletionBlock)completedBlock {
    [self sd_setImageWithURL:url placeholderImage:placeholder options:options progress:nil completed:completedBlock];
}


从上面的代码可知,UIImageView+WebCache.h这个分类中提供了很多API,这些API的区别就在于一些可选项
placeholder: 预览图
options: 选项,具体每个选项的详解可以看这里
completedBlock: 加载完成block回调

但不论是哪个API,最终指向的都是这个方法,并且经过多层跳转

2.统一入口
//API们都走这里
- (void)sd_setImageWithURL:(nullable NSURL *)url
          placeholderImage:(nullable UIImage *)placeholder
                   options:(SDWebImageOptions)options
                  progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                 completed:(nullable SDExternalCompletionBlock)completedBlock {
    [self sd_internalSetImageWithURL:url
                    placeholderImage:placeholder
                             options:options
                        operationKey:nil
                       setImageBlock:nil
                            progress:progressBlock
                           completed:completedBlock];
}
3.入口跳转终点

这个方法贼长,一步一步深入

- (void)sd_internalSetImageWithURL:(nullable NSURL *)url
                  placeholderImage:(nullable UIImage *)placeholder
                           options:(SDWebImageOptions)options
                      operationKey:(nullable NSString *)operationKey
             internalSetImageBlock:(nullable SDInternalSetImageBlock)setImageBlock
                          progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                         completed:(nullable SDExternalCompletionBlock)completedBlock
                           context:(nullable NSDictionary<NSString *, id> *)context {
    
    //获取operationKey,一般情况默认operationKey 为nil
    //为什么是nil?因为sd_setImageWithURL
    NSString *validOperationKey = operationKey ?: NSStringFromClass([self class]);
    //validOperationKey 有值时,取消这个key对应的ImageLoadOperation
    [self sd_cancelImageLoadOperationWithKey:validOperationKey];
    //runtime 动态给分类添加属性
    objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    
    //GCD调度组,一般默认情况context = nil
    //为什么是nil?因为sd_setImageWithURL
    dispatch_group_t group = context[SDWebImageInternalSetImageGroupKey];
    //这里可以理解为options 不包含 SDWebImageDelayPlaceholder
    //我们啥都没传,所以这里会进去
    if (!(options & SDWebImageDelayPlaceholder)) {
        if (group) {
            //group 是nil 所以不进来
            dispatch_group_enter(group);
        }
        
        //宏定义多线程安全处理
        //如果当前的队列 与 传入的队列名字一样,就直接执行这个block,如果不一样,就用异步函数调用,这里传入的是主队列。
        //这里主要是做placeholder预览图的设置
        dispatch_main_async_safe(^{
            [self sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock cacheType:SDImageCacheTypeNone imageURL:url];
        });
    }
    //url为空判断
    if (url) {
#if SD_UIKIT
        // check if activityView is enabled or not
        // 如果TAG_ACTIVITY_SHOW 是true 有活动指示器,就添加活动指示器
        if ([self sd_showActivityIndicatorView]) {
            [self sd_addActivityIndicator];
        }
#endif
        
        // reset the progress
        //重置进度值
        self.sd_imageProgress.totalUnitCount = 0;
        self.sd_imageProgress.completedUnitCount = 0;
        
        //真正的开始搞事
        //manager 初始化依靠这个context ,我们没传context进来,所以这里manager是个nil
        SDWebImageManager *manager = [context objectForKey:SDWebImageExternalCustomManagerKey];
        if (!manager) {
            //获取manager 单例
            manager = [SDWebImageManager sharedManager];
        }
        
        __weak __typeof(self)wself = self;
        //进度block,我们没传progressBlock,所以也不执行,不理他
        SDWebImageDownloaderProgressBlock combinedProgressBlock = ^(NSInteger receivedSize, NSInteger expectedSize, NSURL * _Nullable targetURL) {
            wself.sd_imageProgress.totalUnitCount = expectedSize;
            wself.sd_imageProgress.completedUnitCount = receivedSize;
            if (progressBlock) {
                progressBlock(receivedSize, expectedSize, targetURL);
            }
        };
        

        //初始化operation,并且开始加载图片,图片加载完后,再执行回调。
        //待会儿我们下一段代码就进去这个方法好好看看,先把这一个大方法的注释写完。
        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 (finished && !error && sself.sd_imageProgress.totalUnitCount == 0 && sself.sd_imageProgress.completedUnitCount == 0) {
                sself.sd_imageProgress.totalUnitCount = SDWebImageProgressUnitCountUnknown;
                sself.sd_imageProgress.completedUnitCount = SDWebImageProgressUnitCountUnknown;
            }
            //是否唤起完成回调的bool
            BOOL shouldCallCompletedBlock = finished || (options & SDWebImageAvoidAutoSetImage);
            //是否不设置图片的bool
            BOOL shouldNotSetImage = ((image && (options & SDWebImageAvoidAutoSetImage)) ||
                                      (!image && !(options & SDWebImageDelayPlaceholder)));
            //初始化回调block
            SDWebImageNoParamsBlock callCompletedBlockClojure = ^{
                if (!sself) { return; }
                if (!shouldNotSetImage) {
                    [sself sd_setNeedsLayout];
                }
                if (completedBlock && shouldCallCompletedBlock) {
                    completedBlock(image, error, cacheType, url);
                }
            };
            
            //如果不设置图片就直接执行回调block,
            if (shouldNotSetImage) {
                dispatch_main_async_safe(callCompletedBlockClojure);
                return;
            }
            
            //目标图片
            UIImage *targetImage = nil;
            //目标图片data
            NSData *targetData = nil;
            if (image) {
                //如果有图就赋值给目标图片跟目标data
                targetImage = image;
                targetData = data;
            } else if (options & SDWebImageDelayPlaceholder) {
                // 如果图片是空就把预览图赋值过去,目标data留空
                targetImage = placeholder;
                targetData = nil;
            }
            
#if SD_UIKIT || SD_MAC
            // check whether we should use the image transition
            // 啥都不管,就判断是否要用这个transition
            SDWebImageTransition *transition = nil;
            if (finished && (options & SDWebImageForceTransition || cacheType == SDImageCacheTypeNone)) {
                transition = sself.sd_imageTransition;
            }
#endif
            //CGD线程安全
            dispatch_main_async_safe(^{
                if (group) {
                    dispatch_group_enter(group);
                }
#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 cacheType:cacheType imageURL:imageURL];
#endif
                if (group) {
                    // compatible code for FLAnimatedImage, because we assume completedBlock called after image was set. This will be removed in 5.x
                    BOOL shouldUseGroup = [objc_getAssociatedObject(group, &SDWebImageInternalSetImageGroupKey) boolValue];
                    if (shouldUseGroup) {
                        dispatch_group_notify(group, dispatch_get_main_queue(), callCompletedBlockClojure);
                    } else {
                        callCompletedBlockClojure();
                    }
                } else {
                    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);
            }
        });
    }
}

继续往下走

//继续跳转到下面这个方法
- (void)sd_setImageWithPreviousCachedImageWithURL:(nullable NSURL *)url
                                 placeholderImage:(nullable UIImage *)placeholder
                                          options:(SDWebImageOptions)options
                                         progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                                        completed:(nullable SDExternalCompletionBlock)completedBlock {
    //根据url获取缓存key
    NSString *key = [[SDWebImageManager sharedManager] cacheKeyForURL:url];
    //根据key查找缓存图片
    //imageFromCacheForKey方法会先在内存中找,找不到就继续在disk硬盘中找
    UIImage *lastPreviousCachedImage = [[SDImageCache sharedImageCache] imageFromCacheForKey:key];

    [self sd_setImageWithURL:url placeholderImage:lastPreviousCachedImage ?: placeholder options:options progress:progressBlock completed:completedBlock];    
}

这里通过SDWebImageManager单例执行imageFromCacheForKey方法,根据url查找缓存图片,并赋值给lastPreviousCachedImage。

如果lastPreviousCachedImage不为空,那么lastPreviousCachedImage将传入placeholderImage,作为预览图继续往下执行。

在接下来的方法中,有判断如果有预览图就会优先设置预览图

继续跳转,下面将是一串很长的代码,这里先说一下这么一长串主要都干了什么。

  1. 参数判断
  2. 设置预览图
  3. 初始化SDWebImageManager
  4. 进度block处理
  5. 初始化id <SDWebImageOperation> operation,进行loadImageWithURL
  6. 处理loadImageWithURL参数block回调
  7. 调用完成回调block
- (void)sd_internalSetImageWithURL:(nullable NSURL *)url
                  placeholderImage:(nullable UIImage *)placeholder
                           options:(SDWebImageOptions)options
                      operationKey:(nullable NSString *)operationKey
             internalSetImageBlock:(nullable SDInternalSetImageBlock)setImageBlock
                          progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                         completed:(nullable SDExternalCompletionBlock)completedBlock
                           context:(nullable NSDictionary<NSString *, id> *)context {
    //不管三七二十一,获取这个operationKey,一般sdsetimage的时候是没有的
    NSString *validOperationKey = operationKey ?: NSStringFromClass([self class]);
    //只有validOperationKey 有值的时候,才会做这个操作,一般没有
    [self sd_cancelImageLoadOperationWithKey:validOperationKey];
    //runtime 动态给分类添加属性
    objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    
    //调度组 一般情况 这个ocntext  是nil
    dispatch_group_t group = context[SDWebImageInternalSetImageGroupKey];
    if (!(options & SDWebImageDelayPlaceholder)) {
        //所以这里也不会进组
        if (group) {
            dispatch_group_enter(group);
        }
        
        //宏定义的多线程处理
        //如果当前的队列 与 传入的队列名字一样,就直接执行这个block,如果不一样,就用异步函数调用,这里传入的是主队列。
        //这里主要是做placeholder预览图的设置
        dispatch_main_async_safe(^{
            [self sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock cacheType:SDImageCacheTypeNone imageURL:url];
        });
    }
    //url不为空
    if (url) {
#if SD_UIKIT
        // check if activityView is enabled or not
        // 如果TAG_ACTIVITY_SHOW 是true 有活动指示器,就添加活动指示器
        if ([self sd_showActivityIndicatorView]) {
            [self sd_addActivityIndicator];
        }
#endif
        
        // reset the progress
        //重置进度值
        self.sd_imageProgress.totalUnitCount = 0;
        self.sd_imageProgress.completedUnitCount = 0;
        
        //真正的开始搞事
        //manager 初始化依靠这个context ,第一次执行肯定没有context 嘛,所以这里manager是个nil
        SDWebImageManager *manager = [context objectForKey:SDWebImageExternalCustomManagerKey];
        if (!manager) {
            //获取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;
            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 (finished && !error && sself.sd_imageProgress.totalUnitCount == 0 && sself.sd_imageProgress.completedUnitCount == 0) {
                sself.sd_imageProgress.totalUnitCount = SDWebImageProgressUnitCountUnknown;
                sself.sd_imageProgress.completedUnitCount = SDWebImageProgressUnitCountUnknown;
            }
            //是否唤起完成回调block的bool
            BOOL shouldCallCompletedBlock = finished || (options & SDWebImageAvoidAutoSetImage);
            //是否不设置图片的bool
            BOOL shouldNotSetImage = ((image && (options & SDWebImageAvoidAutoSetImage)) ||
                                      (!image && !(options & SDWebImageDelayPlaceholder)));
            //初始化回调block
            SDWebImageNoParamsBlock callCompletedBlockClojure = ^{
                if (!sself) { return; }
                if (!shouldNotSetImage) {
                    [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
            //如果不设置图片就直接执行回调block,
            if (shouldNotSetImage) {
                dispatch_main_async_safe(callCompletedBlockClojure);
                return;
            }
            //目标图片
            UIImage *targetImage = nil;
            //目标图片data
            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
            //  检查是否使用transition,这里没有传入options,所以不会用
            SDWebImageTransition *transition = nil;
            if (finished && (options & SDWebImageForceTransition || cacheType == SDImageCacheTypeNone)) {
                transition = sself.sd_imageTransition;
            }
#endif
            //判断是否进组,上文已经有说了 不进
            dispatch_main_async_safe(^{
                if (group) {
                    dispatch_group_enter(group);
                }
#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 cacheType:cacheType imageURL:imageURL];
#endif
                //往下就是一系列的回调函数调用
                if (group) {
                    // compatible code for FLAnimatedImage, because we assume completedBlock called after image was set. This will be removed in 5.x
                    BOOL shouldUseGroup = [objc_getAssociatedObject(group, &SDWebImageInternalSetImageGroupKey) boolValue];
                    if (shouldUseGroup) {
                        dispatch_group_notify(group, dispatch_get_main_queue(), callCompletedBlockClojure);
                    } else {
                        callCompletedBlockClojure();
                    }
                } else {
                    callCompletedBlockClojure();
                }
            });
        }];
        //将operation存到SDOperationsDictionary中
        [self sd_setImageLoadOperation:operation forKey:validOperationKey];
    } else {
        //url为空的情况走这里
        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);
            }
        });
    }
}

在上面那么长一串里面有个重要的方法:加载图片

id <SDWebImageOperation> operation = [manager loadImageWithURL:url 
                                                       options:options 
                                                     progress:combinedProgressBlock 
                                                  completed:nil) {}];

进去里面看看都做了什么。又是很长的一串,这里还是先说一下这么一长串主要都干了什么。

  1. 参数判断
  2. 初始化SDWebImageCombinedOperation并添加到runningOperations中
  3. 初始化SDImageCacheOptions 缓存选项
  4. 执行queryCacheOperationForKey 查询缓存,赋值给operation.cacheOperation
  5. 处理queryCacheOperationForKey参数block回调

<1> 如果有缓存就回调函数
<2> 没缓存同时shouldDownload就下载图片
<3> 没缓存又shouldDownload=NO就回调空数据

  1. 调用完成回调block
  2. 从runningOperations 移除当前的operation
- (id <SDWebImageOperation>)loadImageWithURL:(nullable NSURL *)url
                                     options:(SDWebImageOptions)options
                                    progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                                   completed:(nullable SDInternalCompletionBlock)completedBlock {
    //如果没有回调block就直接invoke方法
    NSAssert(completedBlock != nil, @"If you mean to prefetch the image, use -[SDWebImagePrefetcher prefetchURLs] instead");

    //强转url为NSURL类型
    if ([url isKindOfClass:NSString.class]) {
        url = [NSURL URLWithString:(NSString *)url];
    }

    //如果url 还是 !=NSURL 类型 i就设置成Nil
    if (![url isKindOfClass:NSURL.class]) {
        url = nil;
    }

    //初始化一个operation,并且manager赋值
    SDWebImageCombinedOperation *operation = [SDWebImageCombinedOperation new];
    operation.manager = self;

    //判断是否是有效的url
    BOOL isFailedUrl = NO;
    if (url) {
        //self.failedURLsLock 信号量 _failedURLsLock = dispatch_semaphore_create(1);
        //锁,保证线程安全
        LOCK(self.failedURLsLock);
        //判断self.failedURLs 失败的url集合中是否包含这个url
        isFailedUrl = [self.failedURLs containsObject:url];
        UNLOCK(self.failedURLsLock);
    }

    //如果url长度==0,总之就是参数不合理的时候
    if (url.absoluteString.length == 0 || (!(options & SDWebImageRetryFailed) && isFailedUrl)) {
        //发起回调,错误码NSURLErrorFileDoesNotExist,文件不存在
        [self callCompletionBlockForOperation:operation completion:completedBlock error:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil] url:url];
        return operation;
    }
    //如果url正常就继续往下
    //_runningOperationsLock = dispatch_semaphore_create(1); 也是个锁
    LOCK(self.runningOperationsLock);
    //添加operation到集合内
    [self.runningOperations addObject:operation];
    UNLOCK(self.runningOperationsLock);
    
    //url校验
    NSString *key = [self cacheKeyForURL:url];
    
    //一般这里传进来的options 是 0 ,根据options赋值缓存选项
    SDImageCacheOptions cacheOptions = 0;
    if (options & SDWebImageQueryDataWhenInMemory) cacheOptions |= SDImageCacheQueryDataWhenInMemory;
    if (options & SDWebImageQueryDiskSync) cacheOptions |= SDImageCacheQueryDiskSync;
    if (options & SDWebImageScaleDownLargeImages) cacheOptions |= SDImageCacheScaleDownLargeImages;
    
    __weak SDWebImageCombinedOperation *weakOperation = operation;
    
    //检查cacheOperation 缓存
    operation.cacheOperation = [self.imageCache queryCacheOperationForKey:key options:cacheOptions done:^(UIImage *cachedImage, NSData *cachedData, SDImageCacheType cacheType) {
        //延长生命周期
        __strong __typeof(weakOperation) strongOperation = weakOperation;
        //如果这个操作取消了,或者strongOperation==nil,就在runningOperations中移除这个Operation
        if (!strongOperation || strongOperation.isCancelled) {
            [self safelyRemoveOperationFromRunning:strongOperation];
            return;
        }
        
        // 检查是否要下载
        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) {
                [self callCompletionBlockForOperation:strongOperation completion:completedBlock image:cachedImage data:cachedData error:nil cacheType:cacheType finished:YES url:url];
            }

            //根据options设置downloaderOptions
            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;
                downloaderOptions |= SDWebImageDownloaderIgnoreCachedResponse;
            }
            
            __weak typeof(strongOperation) weakSubOperation = strongOperation;
            
            //开始下载,方法返回token ,并且执行block 回调
            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) {
                    //如果有错误的话
                    [self callCompletionBlockForOperation:strongSubOperation completion:completedBlock error:error url:url];
                    BOOL shouldBlockFailedURL;
                    // Check whether we should block failed 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);
                        [self.failedURLs addObject:url];
                        UNLOCK(self.failedURLsLock);
                    }
                }
                else {
                    //成功下载
                    //移除failedURLs中的当前url
                    if ((options & SDWebImageRetryFailed)) {
                        LOCK(self.failedURLsLock);
                        [self.failedURLs removeObject:url];
                        UNLOCK(self.failedURLsLock);
                    }
                    
                    //是否缓存在硬盘
                    BOOL cacheOnDisk = !(options & SDWebImageCacheMemoryOnly);
                    
                    if (self != [SDWebImageManager sharedManager] && self.cacheKeyFilter && downloadedImage) {
                        //根据手机放大图片 就是 2x 3x的操作
                        downloadedImage = [self scaledImageForKey:key image:downloadedImage];
                    }

                    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:)]) {
                        //外部要有遵循@selector(imageManager:transformDownloadedImage:withURL:)这个代理事件才会进来
                        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
                            @autoreleasepool {
                                //代理回调downloadedImage ,外部处理完图片之后赋值给 transformedImage
                                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
                                    // 默认情况cacheSerializer 是nil,所以我们这里直接走else
                                    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];
                                }
                                //完成方法回调
                                [self callCompletionBlockForOperation:strongSubOperation completion:completedBlock image:transformedImage data:downloadedData error:nil cacheType:SDImageCacheTypeNone finished:finished url:url];
                            }
                        });
                    } else {
                        //正常情况
                        if (downloadedImage && finished) {
                            // 默认情况cacheSerializer 是nil
                            if (self.cacheSerializer) {
                                dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
                                    @autoreleasepool {
                                        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) {
                    //从runningOperations 移除当前的operation
                    [self safelyRemoveOperationFromRunning:strongSubOperation];
                }
            }];
        } else if (cachedImage) {
            //如果不用下载,有缓存图片的话,就走这里回调,
            [self callCompletionBlockForOperation:strongOperation completion:completedBlock image:cachedImage data:cachedData error:nil cacheType:cacheType finished:YES url:url];
            //从runningOperations 移除当前的operation
            [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];
            //从runningOperations 移除当前的operation
            [self safelyRemoveOperationFromRunning:strongOperation];
        }
    }];

    return operation;
}

这一长串里,最重要的就是怎么下载图片downloadImageWithURL了,不用想也知道肯定又是一长串。继续列一下

  1. 参数判断
  2. 获取/创建operation(NSOperation)
  3. operation赋值
  4. 将operation添加到downloadQueue、URLOperations中
  5. 将当前completedBlock 添加到callbackBlocks中
  6. 初始化SDWebImageDownloadToken,并赋值相关变量
- (nullable SDWebImageDownloadToken *)downloadImageWithURL:(nullable NSURL *)url
                                                   options:(SDWebImageDownloaderOptions)options
                                                  progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                                                 completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock {
    //url 为空判断
    if (url == nil) {
        if (completedBlock != nil) {
            completedBlock(nil, nil, nil, NO);
        }
        return nil;
    }
    
    //信号量锁
    LOCK(self.operationsLock);
    //通过URLOperations获取operation
    NSOperation<SDWebImageDownloaderOperationInterface> *operation = [self.URLOperations objectForKey:url];
    
    //如果operation是不存在、已完成、已取消的状态
    if (!operation || operation.isFinished || operation.isCancelled) {
        //创建operation
        operation = [self createDownloaderOperationWithUrl:url options:options];
        __weak typeof(self) wself = self;
        operation.completionBlock = ^{
            __strong typeof(wself) sself = wself;
            if (!sself) {
                return;
            }
            LOCK(sself.operationsLock);
            [sself.URLOperations removeObjectForKey:url];
            UNLOCK(sself.operationsLock);
        };
        [self.URLOperations setObject:operation forKey:url];
        
        //加入到operationQueue中,开始执行NSOperation方法
        [self.downloadQueue addOperation:operation];
    }
    else if (!operation.isExecuting) {
        if (options & SDWebImageDownloaderHighPriority) {
            operation.queuePriority = NSOperationQueuePriorityHigh;
        } else if (options & SDWebImageDownloaderLowPriority) {
            operation.queuePriority = NSOperationQueuePriorityLow;
        } else {
            operation.queuePriority = NSOperationQueuePriorityNormal;
        }
    }
    UNLOCK(self.operationsLock);

    //将回调block放到callbackBlocks 下载任务执行结束后,会调用
    id downloadOperationCancelToken = [operation addHandlersForProgress:progressBlock completed:completedBlock];
    
    SDWebImageDownloadToken *token = [SDWebImageDownloadToken new];
    token.downloadOperation = operation;
    token.url = url;
    token.downloadOperationCancelToken = downloadOperationCancelToken;

    return token;
}

到这里,就是一个主流程,但是我们还不清楚将operation添加到队列中后,是怎么下载的,接着将介绍SDWebImageManager。

SDWebImageManager

SDWebImageManager初始化过程.png

可以看到SDWebImageManager中除了自身还包含了
SDImageCache缓存器
SDWebImageDownloader下载器
一个一个往里剖析先看看SDWebImageManager初始化

//单例对象
+ (nonnull instancetype)sharedManager {
    static dispatch_once_t once;
    static id instance;
    dispatch_once(&once, ^{
        instance = [self new];
    });
    return instance;
}
//初始化
- (nonnull instancetype)init {
    //通过单例获取SDImageCache
    SDImageCache *cache = [SDImageCache sharedImageCache];
    //通过单例获取SDWebImageDownloader
    SDWebImageDownloader *downloader = [SDWebImageDownloader sharedDownloader];
    //调用方法给SDWebImageManager属性变量赋值
    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];//失败url集合
        _failedURLsLock = dispatch_semaphore_create(1);//操作失败集合时的信号量锁
        _runningOperations = [NSMutableSet new];//运行中的operation集合
        _runningOperationsLock = dispatch_semaphore_create(1);//操作运行中的operation集合时的信号量锁
    }
    return self;
}
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];
}

- (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
        // 创建IO串行队列
        _ioQueue = dispatch_queue_create("com.hackemist.SDWebImageCache", DISPATCH_QUEUE_SERIAL);
        //初始化SDImageCacheConfig
        _config = [[SDImageCacheConfig alloc] init];
        
        // Init the memory cache
        // 初始化内存缓存,里面注册了收到内存警告的通知
        _memCache = [[SDMemoryCache alloc] initWithConfig:_config];
        // 设置名称
        _memCache.name = fullNamespace;

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

        //初始化FileManager
        dispatch_sync(_ioQueue, ^{
            self.fileManager = [NSFileManager new];
        });

#if SD_UIKIT
        // Subscribe to app events
        [[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;
}
SDWebImageDownloader 下载器

//单例对象
+ (nonnull instancetype)sharedDownloader {
    static dispatch_once_t once;
    static id instance;
    dispatch_once(&once, ^{
        instance = [self new];
    });
    return instance;
}

//initWithSession 传入默认session
- (nonnull instancetype)init {
    return [self initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
}

- (nonnull instancetype)initWithSessionConfiguration:(nullable NSURLSessionConfiguration *)sessionConfiguration {
    if ((self = [super init])) {
        //初始化SDWebImageDownloaderOperation
        _operationClass = [SDWebImageDownloaderOperation class];
        //是否解压图片
        _shouldDecompressImages = YES;
        //FIF0
        _executionOrder = SDWebImageDownloaderFIFOExecutionOrder;
        //下载队列
        _downloadQueue = [NSOperationQueue new];
        //最大并发数
        _downloadQueue.maxConcurrentOperationCount = 6;
        //队列名
        _downloadQueue.name = @"com.hackemist.SDWebImageDownloader";
        //url对应的operation 的字典
        _URLOperations = [NSMutableDictionary new];
        //HTTPHeader 头信息
        SDHTTPHeadersMutableDictionary *headerDictionary = [SDHTTPHeadersMutableDictionary dictionary];
        //设置User-Agent 、Accept
        NSString *userAgent = nil;
        //此处省略一堆复杂代码,都是给userAgent赋值
        ...

        headerDictionary[@"User-Agent"] = userAgent;
        headerDictionary[@"Accept"] = @"image/webp,image/*;q=0.8";

        _HTTPHeaders = headerDictionary;
        //信号量锁
        _operationsLock = dispatch_semaphore_create(1);
        //信号量锁
        _headersLock = dispatch_semaphore_create(1);
        //超时时间
        _downloadTimeout = 15.0;
        //属性变量session创建
        [self createNewSessionWithConfiguration:sessionConfiguration];
    }
    return self;
}

- (void)createNewSessionWithConfiguration:(NSURLSessionConfiguration *)sessionConfiguration {
    [self cancelAllDownloads];

    //如果不为nil,就取消所有任务
    if (self.session) {
        [self.session invalidateAndCancel];
    }

    //超时时间
    sessionConfiguration.timeoutIntervalForRequest = self.downloadTimeout;

    //常规初始化一个session
    self.session = [NSURLSession sessionWithConfiguration:sessionConfiguration
                                                 delegate:self
                                            delegateQueue:nil];
}

下载器下载流程

1.当前operation添加到下载队列中

- (nullable SDWebImageDownloadToken *)downloadImageWithURL:(nullable NSURL *)url
                                                   options:(SDWebImageDownloaderOptions)options
                                                  progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                                                 completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock {
    ...
    //加入到下载Queue中,开始执行NSOperation方法
    [self.downloadQueue addOperation:operation];
    ...
}
2.<NSURLSessionTaskDelegate, NSURLSessionDataDelegate>代理事件

<1>SDWebImageDownloader

#pragma mark Helper methods
//下面的代理方法实现,都会传入task来判断是否有这个operation,有的话,将会由这个operation来做代理事件处理。
- (NSOperation<SDWebImageDownloaderOperationInterface> *)operationWithTask:(NSURLSessionTask *)task {
    NSOperation<SDWebImageDownloaderOperationInterface> *returnOperation = nil;
    // 遍历self.downloadQueue.operations
    for (NSOperation<SDWebImageDownloaderOperationInterface> *operation in self.downloadQueue.operations) {
        if ([operation respondsToSelector:@selector(dataTask)]) {
            //看有没有operation的dataTask.taskIdentifier 对应这个dataTask的taskIdentifier
            if (operation.dataTask.taskIdentifier == task.taskIdentifier) {
                returnOperation = operation;
                break;
            }
        }
    }
    return returnOperation;
}

#pragma mark NSURLSessionDataDelegate
//1.接收到服务器的响应 它默认会取消该请求
- (void)URLSession:(NSURLSession *)session
          dataTask:(NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSURLResponse *)response
 completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler {

    // Identify the operation that runs this task and pass it the delegate method
    // 在下载队列中找找看有没有operation的dataTask.taskIdentifier 对应这个dataTask
    NSOperation<SDWebImageDownloaderOperationInterface> *dataOperation = [self operationWithTask:dataTask];
    if ([dataOperation respondsToSelector:@selector(URLSession:dataTask:didReceiveResponse:completionHandler:)]) {
        //如果有就并且有遵循代理事件,将会跳到SDWebImageDownloaderOperation中执行处理事件
        [dataOperation URLSession:session dataTask:dataTask didReceiveResponse:response completionHandler:completionHandler];
    } else {
        if (completionHandler) {
            completionHandler(NSURLSessionResponseAllow);
        }
    }
}
//2.接收到服务器返回的数据 调用多次
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {

    // Identify the operation that runs this task and pass it the delegate method
    NSOperation<SDWebImageDownloaderOperationInterface> *dataOperation = [self operationWithTask:dataTask];
    if ([dataOperation respondsToSelector:@selector(URLSession:dataTask:didReceiveData:)]) {
        //如果有就并且有遵循代理事件,将会跳到SDWebImageDownloaderOperation中执行处理事件
        [dataOperation URLSession:session dataTask:dataTask didReceiveData:data];
    }
}
//3.将会缓存Response
- (void)URLSession:(NSURLSession *)session
          dataTask:(NSURLSessionDataTask *)dataTask
 willCacheResponse:(NSCachedURLResponse *)proposedResponse
 completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler {

    // Identify the operation that runs this task and pass it the delegate method
    NSOperation<SDWebImageDownloaderOperationInterface> *dataOperation = [self operationWithTask:dataTask];
    if ([dataOperation respondsToSelector:@selector(URLSession:dataTask:willCacheResponse:completionHandler:)]) {
        //如果有就并且有遵循代理事件,将会跳到SDWebImageDownloaderOperation中执行处理事件
        [dataOperation URLSession:session dataTask:dataTask willCacheResponse:proposedResponse completionHandler:completionHandler];
    } else {
        if (completionHandler) {
            completionHandler(proposedResponse);
        }
    }
}

#pragma mark NSURLSessionTaskDelegate
//4.任务完成,带上error
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
    
    // Identify the operation that runs this task and pass it the delegate method
    NSOperation<SDWebImageDownloaderOperationInterface> *dataOperation = [self operationWithTask:task];
    if ([dataOperation respondsToSelector:@selector(URLSession:task:didCompleteWithError:)]) {
        //如果有就并且有遵循代理事件,将会跳到SDWebImageDownloaderOperation中执行处理事件
        [dataOperation URLSession:session task:task didCompleteWithError:error];
    }
}

<2>SDWebImageDownloaderOperation
这里只给几个比较重要的代理回调

#pragma mark NSURLSessionDataDelegate
//1.接收到服务器的响应 它默认会取消该请求
- (void)URLSession:(NSURLSession *)session
          dataTask:(NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSURLResponse *)response
 completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler {
    //回调意向
    NSURLSessionResponseDisposition disposition = NSURLSessionResponseAllow;
    //response长度
    NSInteger expected = (NSInteger)response.expectedContentLength;
    expected = expected > 0 ? expected : 0;
    self.expectedSize = expected;
    self.response = response;
    //响应码
    NSInteger statusCode = [response respondsToSelector:@selector(statusCode)] ? ((NSHTTPURLResponse *)response).statusCode : 200;
    //是否合法
    BOOL valid = statusCode < 400;
    //'304 Not Modified' is an exceptional one. It should be treated as cancelled if no cache data
    //URLSession current behavior will return 200 status code when the server respond 304 and URLCache hit. But this is not a standard behavior and we just add a check
    if (statusCode == 304 && !self.cachedData) {
        valid = NO;
    }
    
    if (valid) {
        for (SDWebImageDownloaderProgressBlock progressBlock in [self callbacksForKey:kProgressCallbackKey]) {
            //进度block回调
            progressBlock(0, expected, self.request.URL);
        }
    } else {
        // Status code invalid and marked as cancelled. Do not call `[self.dataTask cancel]` which may mass up URLSession life cycle
        //取消这个响应
        disposition = NSURLSessionResponseCancel;
    }
    __block typeof(self) strongSelf = self;
    //异步主队列调用通知中心,收到服务器响应
    dispatch_async(dispatch_get_main_queue(), ^{
        [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadReceiveResponseNotification object:strongSelf];
    });
    //响应
    if (completionHandler) {
        completionHandler(disposition);
    }
}

//2.接收到服务器返回的数据 调用多次
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
    //初始化imageData
    if (!self.imageData) {
        self.imageData = [[NSMutableData alloc] initWithCapacity:self.expectedSize];
    }
    //拼接数据
    [self.imageData appendData:data];
    //如果有SDWebImageDownloaderProgressiveDownload这个option
    if ((self.options & SDWebImageDownloaderProgressiveDownload) && self.expectedSize > 0) {
        // Get the image data
        __block NSData *imageData = [self.imageData copy];
        // Get the total bytes downloaded
        const NSInteger totalSize = imageData.length;
        // Get the finish status
        BOOL finished = (totalSize >= self.expectedSize);
        
        if (!self.progressiveCoder) {
            // We need to create a new instance for progressive decoding to avoid conflicts
            for (id<SDWebImageCoder>coder in [SDWebImageCodersManager sharedInstance].coders) {
                if ([coder conformsToProtocol:@protocol(SDWebImageProgressiveCoder)] &&
                    [((id<SDWebImageProgressiveCoder>)coder) canIncrementallyDecodeFromData:imageData]) {
                    //编码器
                    self.progressiveCoder = [[[coder class] alloc] init];
                    break;
                }
            }
        }
        
        // progressive decode the image in coder queue
        dispatch_async(self.coderQueue, ^{
            @autoreleasepool {
                //图片处理
                UIImage *image = [self.progressiveCoder incrementallyDecodedImageWithData:imageData finished:finished];
                if (image) {
                    NSString *key = [[SDWebImageManager sharedManager] cacheKeyForURL:self.request.URL];
                    image = [self scaledImageForKey:key image:image];
                    if (self.shouldDecompressImages) {
                        image = [[SDWebImageCodersManager sharedInstance] decompressedImageWithImage:image data:&imageData options:@{SDWebImageCoderScaleDownLargeImagesKey: @(NO)}];
                    }
                    
                    // We do not keep the progressive decoding image even when `finished`=YES. Because they are for view rendering but not take full function from downloader options. And some coders implementation may not keep consistent between progressive decoding and normal decoding.
                    //回调图片
                    [self callCompletionBlocksWithImage:image imageData:nil error:nil finished:NO];
                }
            }
        });
    }

    //遍历回调block,回调下载进度数据
    for (SDWebImageDownloaderProgressBlock progressBlock in [self callbacksForKey:kProgressCallbackKey]) {
        progressBlock(self.imageData.length, self.expectedSize, self.request.URL);
    }
}

//3.将会缓存Response,没做啥
- (void)URLSession:(NSURLSession *)session
          dataTask:(NSURLSessionDataTask *)dataTask
 willCacheResponse:(NSCachedURLResponse *)proposedResponse
 completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler {
    
    NSCachedURLResponse *cachedResponse = proposedResponse;

    if (!(self.options & SDWebImageDownloaderUseNSURLCache)) {
        // Prevents caching of responses
        cachedResponse = nil;
    }
    if (completionHandler) {
        completionHandler(cachedResponse);
    }
}

#pragma mark NSURLSessionTaskDelegate
//4.任务完成,带上error
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
    //通知中心下载完成
    @synchronized(self) {
        self.dataTask = nil;
        __block typeof(self) strongSelf = self;
        dispatch_async(dispatch_get_main_queue(), ^{
            [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:strongSelf];
            if (!error) {
                [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadFinishNotification object:strongSelf];
            }
        });
    }
    
    // make sure to call `[self done]` to mark operation as finished
    // 如果有error
    if (error) {
        //调用带有错误的回调函数
        [self callCompletionBlocksWithError:error];
        //完事
        [self done];
    } else {
        //防数组越界
        if ([self callbacksForKey:kCompletedCallbackKey].count > 0) {
            /**
             *  If you specified to use `NSURLCache`, then the response you get here is what you need.
             */
            __block NSData *imageData = [self.imageData copy];
            self.imageData = nil;
            if (imageData) {
                /**  if you specified to only use cached data via `SDWebImageDownloaderIgnoreCachedResponse`,
                 *  then we should check if the cached data is equal to image data
                 */
                //如果有SDWebImageDownloaderIgnoreCachedResponse这个option
                //就返回一个空的...啥都没有
                if (self.options & SDWebImageDownloaderIgnoreCachedResponse && [self.cachedData isEqualToData:imageData]) {
                    // call completion block with nil
                    [self callCompletionBlocksWithImage:nil imageData:nil error:nil finished:YES];
                    [self done];
                } else {
                    // decode the image in coder queue
                    dispatch_async(self.coderQueue, ^{
                        //大量临时变量用@autoreleasepool
                        @autoreleasepool {
                            //解码
                            UIImage *image = [[SDWebImageCodersManager sharedInstance] decodedImageWithData:imageData];
                            //缓存Url
                            NSString *key = [[SDWebImageManager sharedManager] cacheKeyForURL:self.request.URL];
                            //图片放大2x 3x
                            image = [self scaledImageForKey:key image:image];
                            
                            // Do not force decoding animated images or GIF,
                            // because there has imageCoder which can change `image` or `imageData` to static image, lose the animated feature totally.
                            // 不要强行解码动图
                            // imageCoder会把image跟imageData都转成静态图片,就会动不了
                            BOOL shouldDecode = !image.images && image.sd_imageFormat != SDImageFormatGIF;
                            if (shouldDecode) {
                                if (self.shouldDecompressImages) {
                                    BOOL shouldScaleDown = self.options & SDWebImageDownloaderScaleDownLargeImages;
                                    image = [[SDWebImageCodersManager sharedInstance] decompressedImageWithImage:image data:&imageData options:@{SDWebImageCoderScaleDownLargeImagesKey: @(shouldScaleDown)}];
                                }
                            }
                            CGSize imageSize = image.size;
                            if (imageSize.width == 0 || imageSize.height == 0) {
                                //图片尺寸有问题,就返回错误信息
                                [self callCompletionBlocksWithError:[NSError errorWithDomain:SDWebImageErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey : @"Downloaded image has 0 pixels"}]];
                            } else {
                                //回调正常的图
                                [self callCompletionBlocksWithImage:image imageData:imageData error:nil finished:YES];
                            }
                            //完事
                            [self done];
                        }
                    });
                }
            } else {
                //回调错误信息
                [self callCompletionBlocksWithError:[NSError errorWithDomain:SDWebImageErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey : @"Image data is nil"}]];
                //完事
                [self done];
            }
        } else {
            //完事
            [self done];
        }
    }
}
3.block回调执行设置图片

图片下载完后,将会回到最上层调用设置图片的方法

- (void)sd_internalSetImageWithURL:(nullable NSURL *)url
                  placeholderImage:(nullable UIImage *)placeholder
                           options:(SDWebImageOptions)options
                      operationKey:(nullable NSString *)operationKey
             internalSetImageBlock:(nullable SDInternalSetImageBlock)setImageBlock
                          progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                         completed:(nullable SDExternalCompletionBlock)completedBlock
                           context:(nullable NSDictionary<NSString *, id> *)context {
    ...
    //设置图片:下载好/有缓存/预览图
    [sself sd_setImage:targetImage imageData:targetData basedOnClassOrViaCustomSetImageBlock:setImageBlock transition:transition cacheType:cacheType imageURL:imageURL];
    ...

待续...

上一篇下一篇

猜你喜欢

热点阅读