建议:在开始看之前,先打开一份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,作为预览图继续往下执行。
在接下来的方法中,有判断如果有预览图就会优先设置预览图
继续跳转,下面将是一串很长的代码,这里先说一下这么一长串主要都干了什么。
- 参数判断
- 设置预览图
- 初始化SDWebImageManager
- 进度block处理
- 初始化id <SDWebImageOperation> operation,进行loadImageWithURL
- 处理loadImageWithURL参数block回调
- 调用完成回调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) {}];
进去里面看看都做了什么。又是很长的一串,这里还是先说一下这么一长串主要都干了什么。
- 参数判断
- 初始化SDWebImageCombinedOperation并添加到runningOperations中
- 初始化SDImageCacheOptions 缓存选项
- 执行queryCacheOperationForKey 查询缓存,赋值给operation.cacheOperation
- 处理queryCacheOperationForKey参数block回调
<1> 如果有缓存就回调函数
<2> 没缓存同时shouldDownload就下载图片
<3> 没缓存又shouldDownload=NO就回调空数据
- 调用完成回调block
- 从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
了,不用想也知道肯定又是一长串。继续列一下
- 参数判断
- 获取/创建operation(NSOperation)
- operation赋值
- 将operation添加到downloadQueue、URLOperations中
- 将当前completedBlock 添加到callbackBlocks中
- 初始化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];
...
网友评论