SDWebImage 可以说是用途最广的库了,看了源码之后来说说自己的理解。
SD 架构
首先看 SDWebImage 自己提供的架构图:
仔细观察发现,SDWebImage 按功能,主要分为两块结构。
- UIKit + SD 分类:提供了外部设置图片的接口
- WebImageManager:内部逻辑类
按照平时的使用习惯,可以整理出常用类,如下图:
UIKit + SD 分类
ImageView 分类提供的接口有:
1 | - (void)sd_setImageWithURL:(nullable NSURL *)url; |
Button 分类提供的接口:
1 | - (void)sd_setImageWithURL:(nullable NSURL *)url |
不难看出,设置图片的接口都是通过一个“全能初始化方法”去实现。而设置动图的方法是 UIImageView+WebCache
分类自己实现的。
溯源可知,设置图片的方法在 UIView+WebCache
中实现:
1 | - (void)sd_internalSetImageWithURL:(nullable NSURL *)url |
下面是这个方法的实现,关键点的注释在代码块中:
1 | - (void)sd_internalSetImageWithURL:(nullable NSURL *)url |
上面代码中 1.
的操作:
1 | - (void)sd_cancelImageLoadOperationWithKey:(nullable NSString *)key { |
2.
的设置图片操作:
1 |
|
值得一提的是,SDOperationDictionary
是由 UIView+WebCacheOperation
分类管理的,履行了面向对象单一职责原则,通俗说就是各干各的事,不越权,而且对代码的可读性有很好的帮助。
SDImageCache
SDImageCache 主要职责是管理下载完的图片缓存。在设置图片的时候,SDWebImageManager 首先会访问缓存,如果有缓存,则直接以缓存设置图片。
SDImageCache 是通过 NSCache 来缓存图片的,NSCache 的一个简单的优势就是它是线程安全的。
在 SDImageCache.m
中:
1 | @interface SDImageCache () |
SDMemoryCache 是继承自 NSCache:
1 | // A memory cache which auto purge the cache on memory warning and support weak cache. |
缓存类型
1 | typedef NS_ENUM(NSInteger, SDImageCacheType) { |
存储缓存
通过一个串行队列去执行,一个任务执行完毕才执行下一个,不会出现文件同时被读取和写入的情况:
1 | // Create IO serial queue |
当下载完图片后,会先将图片保存到 NSCache 中,并把图片像素(image.width × image.height × image.scale2)大小作为该对象的 cost 值,同时如果需要保存到硬盘,会先判断图片的格式,PNG 或者 JPEG,并保存对应的 NSData 到缓存路径中,文件名为 URL 的 MD5 值:
1 | - (void)storeImage:(nullable UIImage *)image |
缓存删除
缓存删除的原理是首先移除内存缓存中的图片,如果需要从磁盘中移除,通过 io 串行队列异步操作移除磁盘中的图片 data。
1 | - (void)removeImageForKey:(nullable NSString *)key fromDisk:(BOOL)fromDisk withCompletion:(nullable SDWebImageNoParamsBlock)completion { |
缓存查询
缓存的查询是通过 NSOperation 操作的,SD 定义了一些缓存选项:
1 | typedef NS_OPTIONS(NSUInteger, SDImageCacheOptions) { |
通过 Query 可查到缓存机制如下,首先查询内存中图片,如果只需要从内存中取出图片,则查询完毕立即返回空值。如果需要从磁盘中查询,为了防止数据的错乱,使用了一个 NSOperation 操作,如果有磁盘数据而没有内存数据,则将磁盘数据取出转换成内存数据,并返回。这样做是为了提高查询效率:
1 | - (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key options:(SDImageCacheOptions)options done:(nullable SDCacheQueryCompletedBlock)doneBlock { |
SDWebImageDownloader
SDWebImageDownloader 管理图片的下载和缓存。SDWebImageDownloaderOperation 是继承自 NSOperation 的类。
一些关键的实例变量
1 | /** |
配置 downloadToken 和 downloadOperation
1 | - (nullable SDWebImageDownloadToken *)addProgressCallback:(SDWebImageDownloaderProgressBlock)progressBlock |
下载
downloadImageWithURL: options: progress: completed:
方法是 SDWebImageDownloader 的核心,调用了上面的代码,同时在创建回调的 block 中创建新的操作,配置之后将其放入 downloadQueue 操作队列中,最后方法返回新创建的操作,返回一个遵循 SDWebImageOperation 协议的对象:
1 | - (nullable SDWebImageDownloadToken *)downloadImageWithURL:(nullable NSURL *)url |
SDWebImageManager
SDWebImageManager
管理着 SDImageCache
和 SDWebImageDownloader
。在下载任务开始的时候,manager 先访问 cache 查询是否有可用的缓存,如果有,则直接使用缓存图片。如果没有缓存,就使用 downloader 来下载图片,下载成功后,存入缓存,显示图片。
SDWebImageManager 的核心方法:
1 | - (id <SDWebImageOperation>)loadImageWithURL:(nullable NSURL *)url |
总结
以上就是 SDWebImage 设置图片的主要流程。
基本上可以总结为:
- 外部调用 UIKit+SD 分类中的设置图片方法
- UIKit+SD 的方法调用 SDWebImageManager 的 loadImage 方法去加载图片
- SDWebImage 的 loadImage 方法会先查询是否有缓存,如果有缓存则直接使用缓存图片
- 如果没有缓存,或者缓存需要刷新,则下载图片,重新缓存
- SDImageCache 中的增删查操作都是通过队列加锁来确保操作的安全性