美文网首页
SDWebImage库源代码解析

SDWebImage库源代码解析

作者: percivals | 来源:发表于2021-03-01 09:46 被阅读0次

功能简介

 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实例

  1. 获取图片方法
- (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
  1. 判断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;
}
  1. 集合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;
}
  1. 保存operation
LOCK(self.runningOperationsLock);
[self.runningOperations addObject:operation];
UNLOCK(self.runningOperationsLock);

runningOperations是一个可变数组,保存所有的operation,主要用来监测是否有operation在执行,即判断running 状态。

  1. 查找是否有图片缓存
- (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沙盒图片的查找,找到需要将图片加入内存图片中,内存溢出则先清理一遍内存

  1. 如果没有在缓存中找到图片,或者不管是否找到图片,只要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生成指定路径,去沙盒中查找,如果找到则通过回调返回,如果是从沙盒中读取到了图片,将图片添加到内存缓存中,均未找到则需要去下载图片。

    下载成功后对图片做解码处理,回调返回。并对图片进行缓存,保存到内存中和本地沙盒中。

    如有需要,在展示图片前会做一些图片处理在进行展示,根据配置项来。

参考:

https://www.jianshu.com/p/ff9095de1753

https://www.jianshu.com/p/5baeff2bc9d4

相关文章

网友评论

      本文标题:SDWebImage库源代码解析

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