功能简介
1、一个添加了web图片加载和缓存管理的UIImageView分类
2、一个异步图片下载器
3、一个异步的内存加磁盘综合存储图片并且自动处理过期图片
4、支持动态gif图
5、支持webP格式的图片
6、后台图片解压处理
7、确保同样的图片url不会下载多次
8、确保伪造的图片url不会重复尝试下载
9、确保主线程不会阻塞
SDWebImage加载图片流程
第一步,取消异步下载
**取消当前正在进行的异步下载,确保每个 UIImageView 对象中永远只存在一个 operation,当前只允许一个图片网络请求,该 operation 负责从缓存中获取 image 或者是重新下载 image **
NSString *validOperationKey = operationKey ?: NSStringFromClass([self class]);
// 取消先前下载的任务
[self sd_cancelImageLoadOperationWithKey:validOperationKey];
... // 下载图片操作
// 将生成的加载操作赋值给UIView的自定义属性
[self sd_setImageLoadOperation:operation forKey:validOperationKey];
上述方法定义在UIView+WebCacheOperation
类中
- (void)sd_setImageLoadOperation:(nullable id<SDWebImageOperation>)operation forKey:(nullable NSString *)key {
if (key) {
// 如果之前已经有过该图片的下载操作,则取消之前的图片下载操作
[self sd_cancelImageLoadOperationWithKey:key];
if (operation) {
SDOperationsDictionary *operationDictionary = [self sd_operationDictionary];
@synchronized (self) {
[operationDictionary setObject:operation forKey:key];
}
}
}
}
- (void)sd_cancelImageLoadOperationWithKey:(nullable NSString *)key {
if (key) {
// Cancel in progress downloader from queue
SDOperationsDictionary *operationDictionary = [self sd_operationDictionary]; // 获取添加在UIView的自定义属性
id<SDWebImageOperation> operation;
@synchronized (self) {
operation = [operationDictionary objectForKey:key];
}
if (operation) {
// 实现了SDWebImageOperation的协议
if ([operation conformsToProtocol:@protocol(SDWebImageOperation)]) {
[operation cancel];
}
@synchronized (self) {
[operationDictionary removeObjectForKey:key];
}
}
}
}
实际上,所有的操作都是由一个实际上,所有的操作都是由一个operationDictionary
字典维护的,执行新的操作之前,cancel所有的operation。
第二步 设置占位图 并判断url是否合法
if (!(options & SDWebImageDelayPlaceholder)) {
dispatch_main_async_safe(^{
[self sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock cacheType:SDImageCacheTypeNone imageURL:url];
});
}
判断url是否合法
if (url) {
//下载图片操作
} 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);
}
});
}
#### 第三步 获取图片
获取图片的操作是由SDWebImageManager
完成的,它是一个单例
获取成功后根据枚举类型,判断是否需要设置图片,进行图片的设置操作
** SDWebImageManager ** 用于处理异步下载和图片缓存的类
维护了一个SDImageCache实例和一个SDWebImageDownloader实例
- 获取图片方法
- (nullable id <SDWebImageOperation>)loadImageWithURL:(nullable NSURL *)url options:(SDWebImageOptions)options context:(nullable SDWebImageContext *)context progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock completed:(nullable SDInternalCompletionBlock)completedBlock;
SDWebImageManager.h首先定义了一些枚举类型的SDWebImageOptions
。
然后,声明了四个block
//操作完成的回调,被上层的扩展调用。
typedef void(^SDWebImageCompletionBlock)(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL);
//被SDWebImageManager调用。如果使用了SDWebImageProgressiveDownload标记,这个block可能会被重复调用,直到图片完全下载结束,finished=true,再最后调用一次这个block。
typedef void(^SDWebImageCompletionWithFinishedBlock)(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL);
//SDWebImageManager每次把URL转换为cache key的时候调用,可以删除一些image URL中的动态部分。
typedef NSString *(^SDWebImageCacheKeyFilterBlock)(NSURL *url);
typedef NSData * _Nullable(^SDWebImageCacheSerializerBlock)(UIImage * _Nonnull image, NSData * _Nullable data, NSURL * _Nullable imageURL);
定义了SDWebImageManagerDelegate协议:
@protocol SDWebImageManagerDelegate
@optional
// 控制在cache中没有找到image时 是否应该去下载。
- (BOOL)imageManager:(SDWebImageManager *)imageManager shouldDownloadImageForURL:(NSURL *)imageURL;
// 在下载之后,缓存之前转换图片。在全局队列中操作,不阻塞主线程
- (UIImage *)imageManager:(SDWebImageManager *)imageManager transformDownloadedImage:(UIImage *)image withURL:(NSURL *)imageURL;
@end
- 判断url 的合法性
if ([url isKindOfClass:NSString.class]) {
url = [NSURL URLWithString:(NSString *)url];
}
// Prevents app crashing on argument type error like sending NSNull instead of NSURL
if (![url isKindOfClass:NSURL.class]) {
url = nil;
}
- 集合failedURLs保存之前失败的urls,如果url为空或者url之前失败过且不采用重试策略,直接调用completedBlock返回错误。
BOOL isFailedUrl = NO;
if (url) { // 判断url是否是失败过的url
LOCK(self.failedURLsLock);
isFailedUrl = [self.failedURLs containsObject:url];
UNLOCK(self.failedURLsLock);
}
// 如果url为空或者url下载失败并且设置了不再重试
if (url.absoluteString.length == 0 || (!(options & SDWebImageRetryFailed) && isFailedUrl)) {
[self callCompletionBlockForOperation:operation completion:completedBlock error:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil] url:url];
return operation;
}
- 保存operation
LOCK(self.runningOperationsLock);
[self.runningOperations addObject:operation];
UNLOCK(self.runningOperationsLock);
runningOperations是一个可变数组,保存所有的operation,主要用来监测是否有operation在执行,即判断running 状态。
- 查找是否有图片缓存
- (void)callCacheProcessForOperation:(nonnull SDWebImageCombinedOperation *)operation url:(nonnull NSURL *)url options:(SDWebImageOptions)options context:(nullable SDWebImageContext *)context progress:(nullable SDImageLoaderProgressBlock)progressBlock completed:(nullable SDInternalCompletionBlock)completedBlock
通过cacheKeyForURL:context:方法生成独特的缓存key,用于查找缓存图片
在SDImageCache类里进行图片缓存的查找
- (id<SDWebImageOperation>)queryImageForKey:(NSString *)key options:(SDWebImageOptions)options context:(nullable SDWebImageContext *)context cacheType:(SDImageCacheType)cacheType completion:(nullable SDImageCacheQueryCompletionBlock)completionBlock
通过queryCacheOperationForKey:options:context:cacheType:done: 查找缓存
先进行imageFromMemoryCacheForKey内存图片的查找
未找到则进行diskImageDataBySearchingAllPathsForKey沙盒图片的查找,找到需要将图片加入内存图片中,内存溢出则先清理一遍内存
- 如果没有在缓存中找到图片,或者不管是否找到图片,只要operation有
SDWebImageRefreshCached
标记,那么若SDWebImageManagerDelegate的shouldDownloadImageForURL方法返回true,即允许下载时,都使用SDWebImageManager的下载方法
- (void)callDownloadProcessForOperation:(nonnull SDWebImageCombinedOperation *)operation url:(nonnull NSURL *)url options:(SDWebImageOptions)options context:(SDWebImageContext *)context cachedImage:(nullable UIImage *)cachedImage cachedData:(nullable NSData *)cachedData cacheType:(SDImageCacheType)cacheType progress:(nullable SDImageLoaderProgressBlock)progressBlock completed:(nullable SDInternalCompletionBlock)completedBlock
下载成功后对图片做解码处理,回调返回。并对图片进行缓存,保存到内存中和本地沙盒中。
总结:
先把 placeholderImage 显示出来,再去缓存中查找图片,
先根据一定方法转换生成缓存key来查内存图片,未找到就再根据缓存key生成指定路径,去沙盒中查找,如果找到则通过回调返回,如果是从沙盒中读取到了图片,将图片添加到内存缓存中,均未找到则需要去下载图片。
下载成功后对图片做解码处理,回调返回。并对图片进行缓存,保存到内存中和本地沙盒中。
如有需要,在展示图片前会做一些图片处理在进行展示,根据配置项来。
参考:
网友评论