说明
- SDWebImage地址: https://github.com/rs/SDWebImage
- 版本: 4.1.0
- 只分析 iOS OC 版本的相关实现, MacOS, watchOS等平台以及 Swift 版本暂不涉及, 代码实现大同小异
请求过程
以下面代码为例:
内部调用
项目目录结构及其功能
SDWebImageOperation
: 是一个protocol
, 就定义了一个cancle
方法;SDWebImageCompat
类: 主要是一些不同系统平台下的常用宏定义- Downloader : 下载类,主要是对
NSOperation
以及NSURLSessionTask
以及相关操作 - Cache : 缓存类, 主要是
SDImageCache
以及SDImageCacheConfig
,内部涉及memory cache 和 disk cache的处理 - Utils : 工具类, 主要是管理调用上层接口,以及一些解压图片的工具,外加一个图片预取工具类
- Categories : 项目中使用系统类的扩展方法
- WebCache Categories : 一些 UI 控件的扩展方法,一般是我们直接调用的接口
SDWebImageCompat
兼容各个平台系统的类。
TargetConditionals.h 这个头文件定义了系统常用的宏,比如 CPU 平台,编译器版本等。__OBJC_GC__
这个宏用来判断是否当前平台是否支持GC,诸如此类。FOUNDATION_EXPORT
等同于FOUNDATION_EXTERN
,用来兼容C++,实际代码如下:
重新定义了NS_ENUM的枚举定义,兼容旧语法
快速使用主线程回调的宏:通过dispatch_queue_get_label()
获取当前线程的label
,strcmp()
进行比较主线程的label
,如果是主线程,返回;否则,异步回调到主线程,调用block
根据key
生成图片2x和3x的图片,key
的生成之后可以看到是根据URL生成的FOUNDATION_EXPORT UIImage *SDScaledImageForKey(NSString *key, UIImage *image);
在这个方法的实现中,为了兼容WebP
格式的图片,使用了objc_setAssociatedObject
动态生成一个对象用于存储循环次数。QQ音乐技术团队
的微信号里面这篇文章介绍了WebP这种图片格式的优缺点,可以看一下。
Downloader
主要是两个类SDWebImageDownloader
和SDWebImageDownloaderOperation
SDWebImageDownloader类的功能
包含下载策略的枚举:首先定义了SDWebImageDownloaderOptions
,这是下载策略的定义,初始值使用了位移操作,默认是SDWebImageDownloaderUseNSURLCache
,支持缓存。SDWebImageDownloaderExecutionOrder
这个枚举定义了下载队列的执行策略,分别为FIFO
(队列,默认)和LIFO
(栈)两种。
定义SDWebImageDownloadToken
类,用于标记每一个下载任务,可以用于取消下载。
SDWebImageDownloader类的关键实现
下载器,是一个单例,继承自NSObject
,遵循<NSURLSessionTaskDelegate, NSURLSessionDataDelegate>
协议,重点查看指定初始化方法NS_DESIGNATED_INITIALIZER
用来指明此方法是指定初始化方法。+ (void)initialize;
方法里面对下载开始结束进行监听,并对下载指示器SDNetworkActivityIndicator
进行开启和关闭的操作。
初始化:
关于项目开发中的GCD实战应用有写_barrierQueue的一般用法。
支持方法:
核心方法,下载操作,代码大致如下:
取消和暂停下载操作,主要是对下载队列进行操作,原生的NSOperationQueue
是支持这些操作的,只是API的包装。
SDWebImageDownloaderOperation类
SDWebImageDownloaderOperationInterface
是一个protocol
,定义了SDWebImageDownloaderOperation
的一些重要方法,包括初始化方法以及添加进度的方法
SDWebImageDownloaderOperation
的声明,
其中后两个协议SDWebImageDownloader
也实现了,并且回调中只是把回调传递给operation类,即真实的NSURLSession 的 Delegate方法实际是在operation类中进行回调处理,下载器只是一层回调包装。
查看这个类中的实现,初始化方法中生成一些默认属性以及一个队列
SDWebImageDownloaderOperationInterface
中的一个方法引人注目:
关注开始,取消,重置,完成等方法。
完成:
重置:
取消:
重写了start
方法:
关于并发属性,已经废弃使用concurrent
,需要重写NSOperation的方法,可以用asynchronous
代替
NSURLSessionDataDelegate
方法
接收到服务器响应
首先状态码判断,无状态码或者是400以下并且不是304错误,根据response
取出expectedContentLength
,并赋值给属性expectedSize
,设置对应请求的数据总长度,创建存储imageDate
的可变对象,在主线程发送接收到服务器返回的通知。
否则,取消任务,并且发通知下载停止。
//‘304 Not Modified’ is an exceptional one
接收到数据
这一步进行数据的拼接self.options & SDWebImageDownloaderProgressiveDownload
,并回调下载进度。options默认为0,只有设置过SDWebImageDownloaderProgressiveDownload模式,才会进条件,执行图片的创建,以及解压缩操作。
创建图片的方法:
系统即将缓存
关于证书的处理,具体参考代码实现
Cache
使用SDWebimage的原因,一是支持并发请求,二是支持图片缓存,下面看一下作者是如何构建一个图片下载库的缓存系统的。
Cache文件夹下主要有两个类
SDImageCacheConfig,缓存配置类
这个类简单一些,主要用于配置下载缓存的配置,比如缓存大小(0),缓存时间(默认一周),是否使用内存缓存(YES),是否禁用iCloud(默认禁用),是否解压图片(默认解压)
SDImageCache,缓存类
是个单例,继承自NSObject,属性中有memCache
,用于内存缓存,diskCachePath
,记录文件存储的文件夹,ioQueue
,用于文件读写的队列,还有配置类,maxMemoryCost
,maxMemoryCountLimit
等设置项,customPaths
用于存储那些预取的图片存储路径。
缓存使用内存缓存+文件缓存的策略。
内存缓存使用 NSCache
实现,文件缓存是将图片URL进行处理(MD5)生成唯一文件名,然后存储在磁盘上。
创建,指定初始化方法
分别看图片的操作。
存储,核心方法如下
查询,核心方法
删除
除了上面的核心方法,这个类还提供了一些工具方法,以及一些辅助方法。比如获取缓存文件个数,缓存大小,删除失效的缓存文件,内存警告的时候自动清除缓存,实现原来大概如下:子类化一个NSCache,AutoPurgeCache
,在初始化方法中添加UIApplicationDidReceiveMemoryWarningNotification
观察,当收到系统的内存警告,调用removeAllObjects
清理掉缓存。
Utils
辅助类,主要是一些工具方法
SDWebImageManager,重要的调度类
前两部分的缓存类,下载器类,除了个别API,一般不需要使用者直接调用,而是在这个类中进行使用的。这个类主要包括SDWebImageCombinedOperation
,一个SDWebImageManagerDelegate
和SDWebImageManager
的单例对象。
SDWebImageCombinedOperation 遵循SDWebImageOperation
协议,用于取消下载操作,实现方法里面有置空Block的操作。有一个cacheOperation
的属性,用于存储需要cancle的操作。
SDWebImageManagerDelegate 主要定义了两个方法,一个是控制是否下载缓存中没有的图片,一个是缓存图片之前的调用方法,为防止主线程阻塞,回调是在一个全局队列。
SDWebImageManager的主要构成:
指定初始化方法是必须初始化缓存以及下载器。
主要方法:
SDWebImageDecoder:UIImage(ForceDecode)
是UIImage的一个category。提供一个图片解码的方法,以及一个图片缩放并解码的方法。从缓存中取出来的png或者其他格式的图片,需要转换成位图再进行显示,将其他格式的图片转换为位图,即为解码。通常来说,解码是比较耗时的。上面以及把重绘图片CGBitmapContextCreate
方法解释过了。
下面这篇文章介绍了iOS中图片的解压缩过程。
谈谈 iOS 中图片的解压缩
SDWebImagePrefetcher
预先下载图片可以使用这个类,没什么好看的,内部也是调用SDWebImageManager
对象的loadImageWithURL方法。
Categories
一些类的category方法,主要是用于组织代码,比较独立,和SDWebImageDecoder
相似,跟SDWebImage
的核心实现关联不大。
WebCache Categories
主要的调用接口层。平时加载button或者image view上的图片,都是通过这层接口调用实现的。
也是通过category的方式,给这些类增加方法,具体调用都是调用UIView+WebCache.h
中的这个方法。
总结
本文只是粗略的通读了源码,并未进行深入研究, 难免有疏漏,之后会重新阅读,整理一些阅读源码的收获。大概会从图片加载的流程,多线程管理,缓存设计,接口设计,以及代码组织方面进行分析。之后希望能阅读一下相关功能的其他实现库,做个对比。