SDWebImage 是我们最常用的框架之一。它是一个比较庞大的库,但是整体逻辑却非常清晰。套用官方 github 中的一张整体的流程图可以看到:
从图中可以看出我们通过 UIImageView+WebCache
分类提供的方法调用了 UIView+WebCache
中的方法,进而 SDWebImageManager
接管了加载的整个流程。根据不同的策略决定是否从 Cache 中查找,以及是从 Memory Cache 中查找还是从 Disk Cache 中查找。最终走到 SDWebImageDownloader
中下载图片。下载完成还需要对图片进行缓存。这篇我们先简单看一下 SDWebImageManager
之前的调用过程。
UIView+WebCache
UIImageView+WebCache 的入口方法
UIImageView+WebCache
是我们最常使用的分类,它提供了 SDWebImage 使用的入口方法。一般我们会使用这个方法:
1 | - (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder { |
这个方法省略了配置参数以及进度和完成回调。它会调用 UIView+WebCache
的相应方法:
1 | - (void)sd_setImageWithURL:(nullable NSURL *)url |
UIView+WebCache 入口方法
这一段代码有点长,它会创建一个 SDWebImageManager
实例,用来执行下载和缓存操作。代码中有详尽的注释:
1 | - (void)sd_internalSetImageWithURL:(nullable NSURL *)url |
代码注释非常详尽,主要做了以下几件事:
- 准备加载中与加载完成的回调 block
- 创建
SDWebImageManager
实例,并通过它创建满足SDWebImageOperation
协议的 operation - 将上一步创建的 operation 作为值,默认是当前类名,可通过 options 自定义的字符串作为 key,保存到
SDOperationsDictionary
字典中。
现在还要补充以下几点:
SDOperationsDictionary 的作用
1 | typedef NSMapTable<NSString *, id<SDWebImageOperation>> SDOperationsDictionary; |
我们看到上面的方法会生成一个 validOperationKey
,并且会将 Manager 生成的 Operation 放入通过关联对象保存在每个视图对象中的 SDOperationsDictionary
字典中。那么这个字典存在的意义是什么呢?
绝大多数情况下,我们不需要这个字典。因为一个 UIView 只会下载并展示一张图。但是还是有特殊情况,比如 UIButton,可以设置不同状态下的图片。那么我们就需要一个字典保存不同状态的 Operation 了。
SDWebImageContext 的作用
1 | typedef NSDictionary<SDWebImageContextOption, id> SDWebImageContext; |
一般情况下,我们不会对 SDWebImageContext
做任何操作,直接传入 nil。在上面的方法中:
1 | /// 获取 Operation 对应的 key |
拿到字典中的 SDWebImageContextSetImageOperationKey
对应的字符串,作为 SDOperationsDictionary
中 Operation 的键。用来针对同一 UIView 内的多个图片下载的 Operation 做区分。
设置图片的操作
在设置图片前我们看下两个状态量的设置:
1 | /// 如果完成了,或者 options 不允许自动将下载好的图片设置进去(需要手动设置),那么就需要调用外部传进来的 completeBlock |
看了注释你可能还是很懵。这是在 Operation 执行完成的回调中进行的。其中 finished
和 image
都是 Operation 的回调返回的。
shouldCallCompletedBlock
通过判断是否是 finished
之后,或者传入的 SDWebImageOptions
是否是禁止自动设置图片。如果是,那么就会调用使用者传入的完成回调。
shouldNotSetImage
分为两种情况,一种是下载好了图片,但是 SDWebImageOptions
不让自动设置图片 SDWebImageAvoidAutoSetImage
;或者没有把图片下载下来,并且 SDWebImageOptions
设置为需要延迟加载占位图 SDWebImageDelayPlaceholder
。这两种情况会直接调用完成回调,并且直接就返回了。
如果没有问题,那么就走到了设置图片的方法中:
1 | - (void)sd_setImage:(UIImage *)image imageData:(NSData *)imageData basedOnClassOrViaCustomSetImageBlock:(SDSetImageBlock)setImageBlock transition:(SDWebImageTransition *)transition cacheType:(SDImageCacheType)cacheType imageURL:(NSURL *)imageURL { |
SDWebImageManager
入口方法
SDWebImageManager 是一个单例的对象。外部调用的方法如下:
1 | - (SDWebImageCombinedOperation *)loadImageWithURL:(nullable NSURL *)url |
入口方法可说的不多。主要就是判断一下是不是失败的 url,如果不是就走缓存方法。
SDWebImageCombinedOperation
从命名上也可以看出来,这是一个复合的 Operation。包含了一个缓存用的 cacheOperation
和一个下载用的 loaderOperation
:
1 | @interface SDWebImageCombinedOperation : NSObject <SDWebImageOperation> |
它的 cancel
方法会取消内部的两种 Operation:
1 | - (void)cancel { |
前面的代码中我们可以看到,最终方法返回的就是 SDWebImageCombinedOperation
的实例。也就是说,前面各个视图实例中通过关联对象方式保存的 SDOpeartionsDictionary
字典中保存的就是 SDWebImageCombinedOperation
实例对象。
不仅如此,SDWebImageCombinedOperation
实例,还被保存在了 SDWebImageManager
的 runningOperations
属性中。它会强引用 operation 实例。当 cancel 时,从 runningOperations
数组中移除的时候,由于 SDOpeartionsDictionary
是弱引用的方式保存的 operation,它就会被自动移除。
也就是说,UIView 实例和 Manager 都会将 Operation 保存起来。UIView 实例是弱引用,在 Manager 中移除的时候会自动清空其对应 Operation
SDWebImageOptionsResult
SDWebImageOptionResult
将 options 和 context 进行了整合统一。之后的使用过程中就不会出现 options 和 context 了,而是使用 SDWebImageOptionResult
实例:
1 | @implementation SDWebImageOptionsResult |
执行缓存方法
1 | - (void)callCacheProcessForOperation:(nonnull SDWebImageCombinedOperation *)operation |
透过名字我们就能知道它是执行缓存的方法。能走缓存走缓存,不能走缓存直接走下载。关于缓存的具体实现请看后篇。
执行下载方法
1 | - (void)callDownloadProcessForOperation:(nonnull SDWebImageCombinedOperation *)operation |
从方法名中也能直观理解他是下载相关方法。在下载成功的回调中,会进行图片的存储。
执行存储方法
1 | - (void)callStoreCacheProcessForOperation:(nonnull SDWebImageCombinedOperation *)operation |
存储的方法主要围绕着是否要对图片进行 transform 展开的。在下载好图片之后,可以自定义的将图片进行一些转化,比如我们下载的头像数据,可能会直接切成圆角存储。避免以后每次使用图片都要设置带来的困扰。它是通过 SDImageTransformer
实现的。SDImageTransformer
本身是一个协议。SDWebImage 本身实现了一些,可以通过 context 传入。比如 SDImagePipelineTransformer
,SDImageRoundTransformer
等。要注意,transform 之后的图片并不是以原命名保存的,会加上 transformer 提供的后缀,在获取缓存的时候也会在有自定义 transformer 的时候在 key 上加上 transformer 提供的后缀查询。transformer 的种类如下,实际实用过程中是很实用的:
SDImageTransformer类型 | 作用 |
---|---|
SDImagePipelineTransformer | 可以传入一个NSArray |
SDImageRoundCornerTransformer | 添加圆角 |
SDImageResizingTransformer | 调整大小 |
SDImageCroppingTransformer | 剪裁 |
SDImageFlippingTransformer | 翻转 |
SDImageRotationTransformer | 旋转 |
SDImageTintTransformer | 添加色彩 |
SDImageBlurTransformer | 添加模糊 |
SDImageFilterTransformer | 添加滤镜 |
至此,SDWebImageManager 的 manager 部分就分析完了。后续会有缓存,下载和解码部分。