地址 github.com/SDWebImage/…
首先我觉得应该对该框架有一个宏观层面的了解,根据这个思路,层层去分析。
加载流程
这是官方的时序图。我们看到大致上还有以下的流程。
1.调用- (void)sd_setImageWithURL:(nullable NSURL *)url
(类似的方法有很多种,在UIImageView+WebCache
文件中)。
2.进入?口sd_internalSetImageWithURL
(在UIView+WebCach文件中e)。
3.下载图片。
4.下载之前,先去查找图片(通过url,先查缓存后查磁盘,也可以设置查不查缓存)。
5.如果有的话,返回结果。
6.真正的去下载图片
7.返回下载的结果。
8.将下载的图片存在磁盘和缓存中(可以设置是否存到缓存)。
麻辣鸡上次写的东西 存不上 都没了、、。从写一遍吧
一些要记住的枚举 和 常量##
枚举采用位运算的方式,可以选择多个用 | 隔开。关于位运算,之后再写。 注释写的很清楚了,可以自己看。
typedef NS_OPTIONS(NSUInteger, SDWebImageOptions) {
失败重试,有一个加载失败的url数组。每次都在数组中查询一下,
如果设置失败重试了,就开始下载。如果设置了失败不重试,就不下载。
SDWebImageRetryFailed = 1 << 0,
低优先级
SDWebImageLowPriority = 1 << 1,
只在保存到内存中
SDWebImageCacheMemoryOnly = 1 << 2,
渐进下载,类似于浏览器的的方式。
SDWebImageProgressiveDownload = 1 << 3,
刷新内存,下完完成之后进行比对,如果图片变了 就刷新缓存中的图片。
SDWebImageRefreshCached = 1 << 4,
后台下载,当设置了后台操作的时候,就会自动进行,超期了自动停止
SDWebImageContinueInBackground = 1 << 5,
设置cookies 不知道干啥用的。。
SDWebImageHandleCookies = 1 << 6,
允许无效的证书,如果url是https的,但是没有证书的话,用这个选项。
SDWebImageAllowInvalidSSLCertificates = 1 << 7,
高优先级
SDWebImageHighPriority = 1 << 8,
延迟设置占位图
SDWebImageDelayPlaceholder = 1 << 9,
转换动画图片,当设置了这个,所有的动画图片都会被转换。
SDWebImageTransformAnimatedImage = 1 << 10,
取消下载完成之后 自动设置图片。
SDWebImageAvoidAutoSetImage = 1 << 11,
取消自动缩放,
SDWebImageScaleDownLargeImages = 1 << 12,
当图片在内存中的时候 也去同步查找磁盘。
SDWebImageQueryDataWhenInMemory = 1 << 13,
通常查找磁盘是异步的,可以设置成同步的。
SDWebImageQueryDiskSync = 1 << 14,
只从内存中查找图片
SDWebImageFromCacheOnly = 1 << 15,
转换图片,比如 webp转成png。
SDWebImageForceTransition = 1 << 16
};
typedef NS_OPTIONS(NSUInteger, SDWebImageDownloaderOptions) {
低优先级
SDWebImageDownloaderLowPriority = 1 << 0,
渐进下载
SDWebImageDownloaderProgressiveDownload = 1 << 1,
使用NSURLCache 不知道干啥的,。
SDWebImageDownloaderUseNSURLCache = 1 << 2,
无视NSURLCache的响应数据
SDWebImageDownloaderIgnoreCachedResponse = 1 << 3,
后台下载
SDWebImageDownloaderContinueInBackground = 1 << 4,
设置cookies
SDWebImageDownloaderHandleCookies = 1 << 5,
允许无效的证书
SDWebImageDownloaderAllowInvalidSSLCertificates = 1 << 6,
高优先级
SDWebImageDownloaderHighPriority = 1 << 7,
自动缩放
SDWebImageDownloaderScaleDownLargeImages = 1 << 8,
};
typedef NS_ENUM(NSInteger, SDWebImageDownloaderExecutionOrder) {
队列数据结构 先进先出
(first-in-first-out).
SDWebImageDownloaderFIFOExecutionOrder,
栈结构 后进先出
(last-in-first-out).
SDWebImageDownloaderLIFOExecutionOrder
};
typedef NS_ENUM(NSInteger, SDImageCacheType) {
不缓存
SDImageCacheTypeNone,
磁盘缓存
SDImageCacheTypeDisk,
内存缓存
SDImageCacheTypeMemory
};
typedef NS_OPTIONS(NSUInteger, SDImageCacheOptions) {
在内存中的时候,也去同步查找存盘
SDImageCacheQueryDataWhenInMemory = 1 << 0,
同步去查查找磁盘
SDImageCacheQueryDiskSync = 1 << 1,
取消自动缩放
SDImageCacheScaleDownLargeImages = 1 << 2
};
SD_UIKIT
// iOS and tvOS are very similar, UIKit exists on both platforms
#if TARGET_OS_IOS || TARGET_OS_TV
#define SD_UIKIT 1
#else
#define SD_UIKIT 0
#endif
信号量当做锁来用
#define LOCK(lock) dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
#define UNLOCK(lock) dispatch_semaphore_signal(lock);
复制代码
几个关键的类 简要说明##
SDImageCache 缓存类
内存缓存采用的是NSCache 和 NSMapTable结合的方式。因为NSCache有的时候会自动释放,不受控制。我猜NSMapTable是拉链法的散列表。
YYCache也是继承NSCache,但是是用自己实现的双向链表来完成自定义的内存缓存。
SDWebImageDownloader 下载类
SDWebImageManager 核心类
这个类 是下载和查找的入口。是sd的灵魂。
UIView+WebCache UI层面调用的入口 不管是imageView button,最后都会调用到这里。
根据流程一行一行代码的看
- (void)sd_setImageWithURL:(nullable NSURL *)url
placeholderImage:(nullable UIImage *)placeholder
options:(SDWebImageOptions)options
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDExternalCompletionBlock)completedBlock;
参数没有什么好说的了。
最终会调用以下方法
代码段1
- (void)sd_internalSetImageWithURL:(nullable NSURL *)url
placeholderImage:(nullable UIImage *)placeholder
options:(SDWebImageOptions)options
operationKey:(nullable NSString *)operationKey
setImageBlock:(nullable SDSetImageBlock)setImageBlock
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDExternalCompletionBlock)completedBlock
context:(nullable NSDictionary *)context {
//设置validOperationKey
NSString *validOperationKey = operationKey ?: NSStringFromClass([self class]);
//通过validOperationKey 看有没有在下载的队列中,如果在的话,就取消下载并且从队列中移除。这里用了同步锁来保存线程安安全。
[self sd_cancelImageLoadOperationWithKey:validOperationKey];
绑定新的url
objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
//& 位运算 不知道的同学查找 按位与 就可以了
//如果没有设置延迟设置占位图的话 就立刻设置占位图。
if (!(options & SDWebImageDelayPlaceholder)) {
dispatch_main_async_safe(^{
//如果设置了回调 就通过回调把占位图传回去,若果没设置block
判断是不是UIImageView,是的话就直接设置。
[self sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock];
});
}
if (url) {
#if SD_UIKIT
//如果设置了显示系统菊花,就显示出来。
if ([self sd_showActivityIndicatorView]) {
[self sd_addActivityIndicator];
}
#endif
// 重置进度
self.sd_imageProgress.totalUnitCount = 0;
self.sd_imageProgress.completedUnitCount = 0;
SDWebImageManager *manager;
//如果设置了单例mananger之外的mananger就取出来,赋值。
//sd是允许使用其他方式初始化的manager的。
if ([context valueForKey:SDWebImageExternalCustomManagerKey]) {
manager = (SDWebImageManager *)[context valueForKey:SDWebImageExternalCustomManagerKey];
} else {
//如果没有设置额外的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;
//如果设置了进度block 就回调回去
if (progressBlock) {
progressBlock(receivedSize, expectedSize, targetURL);
}
};
//这个方法的代码在下一段中贴出来讲解。
//可以理解为已经得到了图片。
id 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 the progress not been updated, mark it to complete state
//如果完成了。且没有错误且总数=0.且完成=0,就把进度和数量设置为位置的。真的很严谨。
if (finished && !error && sself.sd_imageProgress.totalUnitCount == 0 && sself.sd_imageProgress.completedUnitCount == 0) {
sself.sd_imageProgress.totalUnitCount = SDWebImageProgressUnitCountUnknown;
sself.sd_imageProgress.completedUnitCount = SDWebImageProgressUnitCountUnknown;
}
//看设置了取消自动赋值吗
BOOL shouldCallCompletedBlock = finished || (options & SDWebImageAvoidAutoSetImage);
//看 需不需要自动赋值
//第一个判断条件。有图片且取消了自动赋值
//第二个判断条件。没图片且没有设置延迟设置占位图
BOOL shouldNotSetImage = ((image && (options & SDWebImageAvoidAutoSetImage)) ||
(!image && !(options & SDWebImageDelayPlaceholder)));
SDWebImageNoParamsBlock callCompletedBlockClojure = ^{
if (!sself) { return; }
//要自动设置图片
if (!shouldNotSetImage) {
//把这个view设置成NeedsLayout,在下一个对应mode的Runloop循环的时候,会去重新计算。
[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
if (shouldNotSetImage) {
//如果不用去设置图片 调用block 结束了
dispatch_main_async_safe(callCompletedBlockClojure);
return;
}
UIImage *targetImage = nil;
NSData *targetData = nil;
if (image) {
// case 2a: we got an image and the SDWebImageAvoidAutoSetImage is not set
//解释的很清楚了。。
targetImage = image;
targetData = data;
} else if (options & SDWebImageDelayPlaceholder) {
// 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
SDWebImageTransition *transition = nil;
//如果设置了图片转换或者不缓存,不缓存这个条件我没懂
if (finished && (options & SDWebImageForceTransition || cacheType == SDImageCacheTypeNone)) {
transition = sself.sd_imageTransition;
}
#endif
dispatch_main_async_safe(^{
#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];
#endif
//完成的回调
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);
}
});
}
}
复制代码
代码段2
这段代码完成了,查找和下载的操作
- (id )loadImageWithURL:(nullable NSURL *)url
options:(SDWebImageOptions)options
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDInternalCompletionBlock)completedBlock {
// Invoking this method without a completedBlock is pointless
//断言 参数不符合的话 就崩溃在这里。
NSAssert(completedBlock != nil, @"If you mean to prefetch the image, use -[SDWebImagePrefetcher prefetchURLs] instead");
//使用者总是有这个错误。。。没办法。。作者屈服了 加了判断。
if ([url isKindOfClass:NSString.class]) {
url = [NSURL URLWithString:(NSString *)url];
}
// 保护程序 防止崩溃 null 就炸了。
if (![url isKindOfClass:NSURL.class]) {
url = nil;
}
SDWebImageCombinedOperation *operation = [SDWebImageCombinedOperation new];
operation.manager = self;
//看是不是这个url在不在下载失败的数组里
BOOL isFailedUrl = NO;
if (url) {
//用信号量 保证线程安全。
LOCK(self.failedURLsLock);
isFailedUrl = [self.failedURLs containsObject:url];
UNLOCK(self.failedURLsLock);
}
//url 没有值,失败不重试,在下载失败的数组中
if (url.absoluteString.length == 0 || (!(options & SDWebImageRetryFailed) && isFailedUrl)) {
//完成回调 , 设置error , 返回结束。
[self callCompletionBlockForOperation:operation completion:completedBlock error:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil] url:url];
return operation;
}
//先锁,然后再操作队列中加入本次操作。
LOCK(self.runningOperationsLock);
[self.runningOperations addObject:operation];
UNLOCK(self.runningOperationsLock);
//通过url 返回key,这个转化规则,可以自定义规则。
NSString *key = [self cacheKeyForURL:url];
SDImageCacheOptions cacheOptions = 0;
//根据SDWebImageOptions 设置 SDImageCacheOptions。
// cacheOptions|= SDImageCacheQueryDataWhenInMemory 做位或计算,将结果赋值给cacheOptions
// 等价于 cacheOptions = cacheOptions | SDImageCacheQueryDataWhenInMemory;
// 按位或 每一位都进行比较 相同的位都是1 ,结果的那一位就是1.排除其他的影响。
if (options & SDWebImageQueryDataWhenInMemory) cacheOptions |= SDImageCacheQueryDataWhenInMemory;
if (options & SDWebImageQueryDiskSync) cacheOptions |= SDImageCacheQueryDiskSync;
if (options & SDWebImageScaleDownLargeImages) cacheOptions |= SDImageCacheScaleDownLargeImages;
__weak SDWebImageCombinedOperation *weakOperation = operation;
//去查找缓存 代码段3详细说明。这里可以先看做已经查到了。
operation.cacheOperation = [self.imageCache queryCacheOperationForKey:key options:cacheOptions done:^(UIImage *cachedImage, NSData *cachedData, SDImageCacheType cacheType) {
__strong __typeof(weakOperation) strongOperation = weakOperation;
if (!strongOperation || strongOperation.isCancelled) {
//如果操作被取消了 就安全的从操作队列中移除。
[self safelyRemoveOperationFromRunning:strongOperation];
return;
}
// 看需不需要从网络下载图片。
//条件1 不是只从内存中查找。
//条件2 缓存中没有图片,或者刷新内存中的图片
//条件3 设置了下载的代理
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) {
// If image was found in the cache but SDWebImageRefreshCached is provided, notify about the cached image
// AND try to re-download it in order to let a chance to NSURLCache to refresh it from server.
//很详细 不翻译了
[self callCompletionBlockForOperation:strongOperation completion:completedBlock image:cachedImage data:cachedData error:nil cacheType:cacheType finished:YES url:url];
}
// download if no image or requested to refresh anyway, and download allowed by delegate
SDWebImageDownloaderOptions downloaderOptions = 0;
//根据SDWebImageOptions 设置 SDWebImageDownloaderOptions。
// cacheOptions|= SDImageCacheQueryDataWhenInMemory 做位或计算,将结果赋值给cacheOptions
// 等价于 cacheOptions = cacheOptions | SDImageCacheQueryDataWhenInMemory;
// 按位或 每一位都进行比较 相同的位都是1 ,结果的那一位就是1.排除其他的影响。
if (options & SDWebImageLowPriority) downloaderOptions |= SDWebImageDownloaderLowPriority;
if (options & SDWebImageProgressiveDownload) downloaderOptions |= SDWebImageDownloaderProgressiveDownload;
if (options & SDWebImageRefreshCached) downloaderOptions |= SDWebImageDownloaderUseNSURLCache;
if (options & SDWebImageContinueInBackground) downloaderOptions |= SDWebImageDownloaderContinueInBackground;
if (options & SDWebImageHandleCookies) downloaderOptions |= SDWebImageDownloaderHandleCookies;
if (options & SDWebImageAllowInvalidSSLCertificates) downloaderOptions |= SDWebImageDownloaderAllowInvalidSSLCertificates;
if (options & SDWebImageHighPriority) downloaderOptions |= SDWebImageDownloaderHighPriority;
if (options & SDWebImageScaleDownLargeImages) downloaderOptions |= SDWebImageDownloaderScaleDownLargeImages;
if (cachedImage && options & SDWebImageRefreshCached) {
// force progressive off if image already cached but forced refreshing
//downloaderOptions = ~(downloaderOptions & SDWebImageDownloaderProgressiveDownload)取反
downloaderOptions &= ~SDWebImageDownloaderProgressiveDownload;
// ignore image read from NSURLCache if image if cached but force refreshing
downloaderOptions |= SDWebImageDownloaderIgnoreCachedResponse;
}
// `SDWebImageCombinedOperation` -> `SDWebImageDownloadToken` -> `downloadOperationCancelToken`, which is a `SDCallbacksDictionary` and retain the completed block below, so we need weak-strong again to avoid retain cycle
__weak typeof(strongOperation) weakSubOperation = strongOperation;
//真正开始下载
strongOperation.downloadToken = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage *downloadedImage, NSData *downloadedData, NSError *error, BOOL finished) {
__strong typeof(weakSubOperation) strongSubOperation = weakSubOperation;
//下载操作取消了 什么也不做 github的ieesu 699 有详细说明
if (!strongSubOperation || strongSubOperation.isCancelled) {
// Do nothing if the operation was cancelled
// See #699 for more details
// if we would call the completedBlock, there could be a race condition between this block and another completedBlock for the same object, so if this one is called second, we will overwrite the new data
} else if (error) {
//错误了 返回
[self callCompletionBlockForOperation:strongSubOperation completion:completedBlock error:error url:url];
BOOL shouldBlockFailedURL;
// Check whether we should block failed url
//如果需要返回错误的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);
//添加到失败url的数组
[self.failedURLs addObject:url];
UNLOCK(self.failedURLsLock);
}
}
//没有错误
else {
//设置了失败重试的话 ,把url 从失败的数组中移除。
if ((options & SDWebImageRetryFailed)) {
LOCK(self.failedURLsLock);
[self.failedURLs removeObject:url];
UNLOCK(self.failedURLsLock);
}
//是否缓存到磁盘中 只有设置了SDWebImageCacheMemoryOnly 才不存到磁盘。
BOOL cacheOnDisk = !(options & SDWebImageCacheMemoryOnly);
// We are done the scale process in SDWebImageDownloader with the shared manager, this is used for custom manager and avoid extra scale.
//下载过程中,已经进行了缩放,如果有自定义的manager ,避免重复的缩放。
if (self != [SDWebImageManager sharedManager] && self.cacheKeyFilter && downloadedImage) {
downloadedImage = [self scaledImageForKey:key image:downloadedImage];
}
//NSURLCache 缓存命中
if (options & SDWebImageRefreshCached && cachedImage && !downloadedImage) {
// Image refresh hit the NSURLCache cache, do not call the completion block
//如果图片下载完成 且 下载的图片只有一个 或者 设置了SDWebImageTransformAnimatedImage
} else if (downloadedImage && (!downloadedImage.images || (options & SDWebImageTransformAnimatedImage)) && [self.delegate respondsToSelector:@selector(imageManager:transformDownloadedImage:withURL:)]) {
//出来图片转换之后的情况
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
UIImage *transformedImage = [self.delegate imageManager:self transformDownloadedImage:downloadedImage withURL:url];
if (transformedImage && finished) {
//检查图片有没有被编辑过
BOOL imageWasTransformed = ![transformedImage isEqual:downloadedImage];
NSData *cacheData;
// pass nil if the image was transformed, so we can recalculate the data from the image
// 如果设置了全局的解析转换
if (self.cacheSerializer) {
cacheData = self.cacheSerializer(transformedImage, (imageWasTransformed ? nil : downloadedData), url);
} else {
cacheData = (imageWasTransformed ? nil : downloadedData);
}
//缓存 请看代码段4
[self.imageCache storeImage:transformedImage imageData:cacheData forKey:key toDisk:cacheOnDisk completion:nil];
}
//完成的block回调
[self callCompletionBlockForOperation:strongSubOperation completion:completedBlock image:transformedImage data:downloadedData error:nil cacheType:SDImageCacheTypeNone finished:finished url:url];
});
} else {
//下载图片成功 且 完成
if (downloadedImage && finished) {
if (self.cacheSerializer) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
NSData *cacheData = self.cacheSerializer(downloadedImage, downloadedData, url);
[self.imageCache storeImage:downloadedImage imageData:cacheData forKey:key toDisk:cacheOnDisk completion:nil];
});
} else {
//保存内存和磁盘
[self.imageCache storeImage:downloadedImage imageData:downloadedData forKey:key toDisk:cacheOnDisk completion:nil];
}
}
[self callCompletionBlockForOperation:strongSubOperation completion:completedBlock image:downloadedImage data:downloadedData error:nil cacheType:SDImageCacheTypeNone finished:finished url:url];
}
}
if (finished) {
[self safelyRemoveOperationFromRunning:strongSubOperation];
}
}];
//图片在内存中
} else if (cachedImage) {
[self callCompletionBlockForOperation:strongOperation completion:completedBlock image:cachedImage data:cachedData error:nil cacheType:cacheType finished:YES url:url];
[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];
[self safelyRemoveOperationFromRunning:strongOperation];
}
}];
return operation;
}
复制代码
代码段3 下载
- (nullable SDWebImageDownloadToken *)downloadImageWithURL:(nullable NSURL *)url
options:(SDWebImageDownloaderOptions)options
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock {
__weak SDWebImageDownloader *wself = self;
return [self addProgressCallback:progressBlock completedBlock:completedBlock forURL:url createCallback:^SDWebImageDownloaderOperation *{
__strong __typeof (wself) sself = wself;
NSTimeInterval timeoutInterval = sself.downloadTimeout;
if (timeoutInterval == 0.0) {
timeoutInterval = 15.0;
}
// In order to prevent from potential duplicate caching (NSURLCache + SDImageCache) we disable the cache for image requests if told otherwise
NSURLRequestCachePolicy cachePolicy = options & SDWebImageDownloaderUseNSURLCache ? NSURLRequestUseProtocolCachePolicy : NSURLRequestReloadIgnoringLocalCacheData;
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url
cachePolicy:cachePolicy
timeoutInterval:timeoutInterval];
request.HTTPShouldHandleCookies = (options & SDWebImageDownloaderHandleCookies);
request.HTTPShouldUsePipelining = YES;
if (sself.headersFilter) {
request.allHTTPHeaderFields = sself.headersFilter(url, [sself allHTTPHeaderFields]);
}
else {
request.allHTTPHeaderFields = [sself allHTTPHeaderFields];
}
SDWebImageDownloaderOperation *operation = [[sself.operationClass alloc] initWithRequest:request inSession:sself.session options:options];
operation.shouldDecompressImages = sself.shouldDecompressImages;
if (sself.urlCredential) {
operation.credential = sself.urlCredential;
} else if (sself.username && sself.password) {
operation.credential = [NSURLCredential credentialWithUser:sself.username password:sself.password persistence:NSURLCredentialPersistenceForSession];
}
if (options & SDWebImageDownloaderHighPriority) {
operation.queuePriority = NSOperationQueuePriorityHigh;
} else if (options & SDWebImageDownloaderLowPriority) {
operation.queuePriority = NSOperationQueuePriorityLow;
}
if (sself.executionOrder == SDWebImageDownloaderLIFOExecutionOrder) {
// Emulate LIFO execution order by systematically adding new operations as last operation's dependency
[sself.lastAddedOperation addDependency:operation];
sself.lastAddedOperation = operation;
}
return operation;
}];
}
复制代码
代码段4 缓存
- (void)storeImage:(nullable UIImage *)image
imageData:(nullable NSData *)imageData
forKey:(nullable NSString *)key
toDisk:(BOOL)toDisk
completion:(nullable SDWebImageNoParamsBlock)completionBlock {
if (!image || !key) {
if (completionBlock) {
completionBlock();
}
return;
}
// if memory cache is enabled
if (self.config.shouldCacheImagesInMemory) {
//计算大小
NSUInteger cost = SDCacheCostForImage(image);
[self.memCache setObject:image forKey:key cost:cost];
}
if (toDisk) {
//ioQueue 队列 先进先出 一个一个来 保证顺序
dispatch_async(self.ioQueue, ^{
@autoreleasepool {
NSData *data = imageData;
if (!data && image) {
// If we do not have any data to detect image format, check whether it contains alpha channel to use PNG or JPEG format
SDImageFormat format;
if (SDCGImageRefContainsAlpha(image.CGImage)) {
format = SDImageFormatPNG;
} else {
format = SDImageFormatJPEG;
}
data = [[SDWebImageCodersManager sharedInstance] encodedDataWithImage:image format:format];
}
[self _storeImageDataToDisk:data forKey:key];
}
if (completionBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
completionBlock();
});
}
});
} else {
if (completionBlock) {
completionBlock();
}
}
}
复制代码