SDWebImage源码解析(一)

SDWebImage是一个图片下载的开源项目,由于它提供了简介的接口以及异步下载与缓存的强大功能,深受“猿媛“的喜爱。截止到本篇文章开始,项目的star数已经超过1.6k了。今天我就对项目的源码做个阅读笔记,一方面归纳总结自己的心得,另一方面给准备阅读源码的童鞋做点铺垫工作。代码最新版本为3.8。

正如项目的第一句介绍一样:

Asynchronous image downloader with cache support as a UIImageView category

SDWebImage是个支持异步下载与缓存的UIImageView扩展。项目主要提供了一下功能:

  • 扩展UIImageView, UIButton, MKAnnotationView,增加网络图片与缓存管理。
  • 一个异步的图片加载器
  • 一个异步的 内存 + 磁盘 图片缓存,拥有自动的缓存过期处理机制。
  • 支持后台图片解压缩处理
  • 确保同一个 URL 的图片不被多次下载
  • 确保虚假的 URL 不会被反复加载
  • 确保下载及缓存时,主线程不被阻塞
  • 使用 GCD 与 ARC

项目支持的图片格式包括PNG,JEPG,GIF,WebP等等。

先看看SDWebImage的项目组织架构:

111136939-dedbf4b5f8eaa701

SDWebImage组织架构.png

SDWebImageDownloader负责维持图片的下载队列;
SDWebImageDownloaderOperation负责真正的图片下载请求;
SDImageCache负责图片的缓存;
SDWebImageManager是总的管理类,维护了一个SDWebImageDownloader实例和一个SDImageCache实例,是下载与缓存的桥梁;
SDWebImageDecoder负责图片的解压缩;
SDWebImagePrefetcher负责图片的预取;
UIImageView+WebCache和其他的扩展都是与用户直接打交道的。

其中,最重要的三个类就是SDWebImageDownloaderSDImageCacheSDWebImageManager。接下来我们就分别详细地研究一下这些类各自具体做了哪些事,又是怎么做的。

为了便于大家从宏观上有个把握,我这里先给出项目的框架结构:

121136939-6046837ca4d764b0

UIImageView+WebCacheUIButton+WebCache直接为表层的 UIKit框架提供接口, 而 SDWebImageManger负责处理和协调SDWebImageDownloaderSDWebImageCache, 并与 UIKit层进行交互。SDWebImageDownloaderOperation真正执行下载请求;最底层的两个类为高层抽象提供支持。
我们按照从上到下执行的流程来研究各个类

UIImageView+WebCache

这里,我们只用UIImageView+WebCache来举个例子,其他的扩展类似。
常用的场景是已知图片的url地址,来下载图片并设置到UIImageView上。UIImageView+WebCache提供了一系列的接口:

这些接口最终会调用

方法的第一行代码[self sd_cancelCurrentImageLoad]是取消UIImageView上当前正在进行的异步下载,确保每个 UIImageView 对象中永远只存在一个 operation,当前只允许一个图片网络请求,该 operation 负责从缓存中获取 image 或者是重新下载 image。具体执行代码是:

实际上,所有的操作都是由一个operationDictionary字典维护的,执行新的操作之前,先cancel所有的operation。这里的cancel是SDWebImageOperation协议里面定义的。

是一种占位图策略,作为图片下载完成之前的替代图片。dispatch_main_async_safe是一个宏,保证在主线程安全执行,最后再讲。
然后判断url,url为空就直接调用完成回调,报告错误信息;否则,用SDWebImageManager单例的

方法下载图片。下载完成之后刷新UIImageView的图片。

最后,把返回的id operation添加到operationDictionary中,方便后续的cancel。

SDWebImageManager

SDWebImageManager.h中是这样描述SDWebImageManager类的:

The SDWebImageManager is the class behind the UIImageView+WebCache category and likes.It ties the asynchronous downloader (SDWebImageDownloader) with the image cache store (SDImageCache).You can use this class directly to benefit from web image downloading with caching in another context than a UIView.

即隐藏在UIImageView+WebCache背后,用于处理异步下载和图片缓存的类,当然你也可以直接使用 SDWebImageManager 的方法 downloadImageWithURL:options:progress:completed:来直接下载图片。

SDWebImageManager.h首先定义了一些枚举类型的SDWebImageOptions。关于这些Options的具体含义可以参考叶孤城大神的解析

然后,声明了三个block:

定义了SDWebImageManagerDelegate协议:

SDWebImageManager是单例使用的,分别维护了一个SDImageCache实例和一个SDWebImageDownloader实例。 类方法分别是:

我们主要研究

首先,监测url 的合法性:

第一个判断条件是防止很多用户直接传递NSString作为NSURL导致的错误,第二个判断条件防止crash。

集合failedURLs保存之前失败的urls,如果url为空或者url之前失败过且不采用重试策略,直接调用completedBlock返回错误。

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

SDWebImageManager会首先在memory以及disk的cache中查找是否下载过相同的照片,即调用imageCache

方法。
如果在缓存中找到图片,直接调用completedBlock,第一个参数是缓存的image。

如果没有在缓存中找到图片,或者不管是否找到图片,只要operation有SDWebImageRefreshCached标记,那么若SDWebImageManagerDelegateshouldDownloadImageForURL方法返回true,即允许下载时,都使用 imageDownloader

方法进行下载。如果下载有错误,直接调用completedBlock返回错误,并且视情况将url添加到failedURLs里面;

如果下载成功,若支持失败重试,将url从failURLs里删除:

如果delegate实现了,imageManager:transformDownloadedImage:withURL:方法,图片在缓存之前,需要做转换(在全局队列中调用,不阻塞主线程)。转化成功切下载全部结束,图片存入缓存,调用completedBlock回调,第一个参数是转换后的image。

否则,直接存入缓存,调用completedBlock回调,第一个参数是下载的原始image。

存入缓存都是调用imageCache

方法。

如果没有在缓存找到图片,且不允许下载,直接调用completedBlock,第一个参数为nil。

最后都要将这个operation从runningOperations里删除。

这里再说一下上面的operation,是一个SDWebImageCombinedOperation实例:

是一个遵循SDWebImageOperation协议的NSObject子类。

在里面封装一个NSOperation,这么做的目的应该是为了使代码更简洁。因为下载操作需要查询缓存的operation和实际下载的operation,这个类的cancel方法可以同时cancel两个operation,同时还可以维护一个状态cancelled。
敬请期待后续更新!

1 1 收藏 评论

相关文章

可能感兴趣的话题



直接登录
跳到底部
返回顶部