美文网首页
SDWebImage源码解读(设置图片流程)

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

作者: 尤先森 | 来源:发表于2019-04-08 11:42 被阅读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];
        ...
    

    待续...

    相关文章

      网友评论

          本文标题:SDWebImage源码解读(设置图片流程)

          本文链接:https://www.haomeiwen.com/subject/ybqtbqtx.html