SDWebImage 源代码剖析-缓存策略

在上一篇文章中,我们对Masonry 的源代码进行了分析。今天我们将对另外一个在iOS 开发中广泛使用的库的源代码进行分析,这个库就是鼎鼎大名的SDWebImage。事实上SDWebImage 是如此常用和好用,以至于没有听说过没有用过没有分析过这个库的人应该是少数了。下面是我个人对这个库的缓存策略部分的代码分析和理解。由于本人才疏学浅,若有错误,请大家及时指出,不吝赐教。

使用方法

SDWebImage 的使用非常简洁,往往可以用一行代码来完成图片设置工作。下面列出一些常用设置方法。

url 是远程图片的url 地址,placeholderImage 是远程图片尚未下载完成时显示的占位图片,completedBlock 是远程图片下载完成后将要执行的block,options 是一组NS_OPTIONS枚举值:

内部实现

UIView+WebCache

上面列出的方法其实是一个核心方法接受不同参数时的不同版本。

这个核心方法内部是这样实现的:

进入sd_internalSetImageWithURL: 这个方法的内部,我们总算看到了一段期望中的复杂代码:

  1. 用当前视图的类名来作为一个key。SDWebImage 不仅能用来设置UIImageView,也可以用来设置UIButton。
  2. 在当前视图的operationDictionary 中进行查找,如果已经有key为operationKey 的operation,则取消这个operation。
  3. 将该远程图片的url 与当前视图的imageURLKey 用关联对象设置在一起。关于关联对象,网上也已经有很多不错的分析文章。
  4. 如果没有设置SDWebImageDelayPlaceholder这个选项,那么就先将当前视图设置成placeholder。
  5. 使用SDWebImageManagerloadImageWithURL: 创建一个operation 对象。
  6. 如果image下载完成了,并且设置了SDWebImageAvoidAutoSetImage选项,而且传入了对下载的图片进行处理的block,那么就进行对应处理。
  7. 如果image下载完成了,没有额外处理要求,那么将当前视图设置为image。
  8. 如果image下载失败了,那么还是将当前视图设置为placeholder。
  9. 将5中创建的operation 的key 设置为operationKey,然后加入当前视图的operationDictionary 中。

SDWebImageManager

由代码可知,operation 这个对象是设置过程的核心与关键。既然它是SDWebImageManager创建的,我们自然要去探究下SDWebImageManager的内部秘密。

1

首先进行异常处理。这是编程时一个常见的习惯,将可能遇到的各种问题和对应的解决方案放在方法的开头,可以使得逻辑变得清晰,同时也避免了无谓的函数调用开销。SDWebImage 团队贴心地为我们处理了常见的误将NSString 类型的对象传入NSURL 类型的参数的错误。这启示我们,在编写自己的库时,应尽可能考虑到各种常见错误,并对它们进行处理,这样可以使得你的库对于别的开发者更加友好。

2

将operation 加入SDWebImageManager 的runningOperations 数组中。

3

获得远程图片url 所对应的key。

4

先查询operation 的缓存操作。

在查询到operation 的缓存操作后,设置doneBlock:

  1. 由if条件,假设后一条件成立,如果缓存存在,那么只有在设置了SDWebImageRefreshCache才会进入if 语句体中;如果缓存不存在,那么肯定会进入if 语句体中。
  2. 如果说缓存存在,而且设置了SDWebImageRefreshCache,那么就应该从server 上重新下载图片以更新本地缓存。
  3. 设置下载时的选项。
  4. 如果设置了SDWebImageRefreshCache,那么必须取消渐进式下载,而且还要忽略从NSURLCache 中获得的缓存响应。(此处我还没有理解到底是怎么回事,等了解了NSURLCache 的知识后再来填这个坑)
  5. 调用imageDownloader 进行图片下载。
  6. 如果发生了错误,且错误原因不是列出来的这些原因中的一个,那么就把这个url 加入黑名单中。
  7. 如果缓存存在,设置了SDWebImageRefreshCache,而且downloadedImage为nil,那么说明命中了
    NSURLCache 缓存,什么事也不做。
  8. 如果downloadedImage非nil,并且设置了SDWebImageTransformAnimatedImage,那么就在主线程中对图片进行变换,然后将变换后的图片存储在内存中或内存和磁盘上。如果不需要变换,那么直接将downloadedImage 存储在内存中或内存和磁盘上。
  9. 如果缓存存在,且其他条件都不成立,那么直接取出缓存中的图片,然后在主线程中更新视图。
  10. 如果缓存不存在,并且也不允许下载,那么直接调用completedBlock。

缓存策略小结

我们来小结一下SDWebImage的缓存策略。在给一个UIimageView 或者UIButton 设置了远端图片的url 后,SDWebImage首先以url 为key 在内存中寻找图片缓存,如果在内存中没找到就会去磁盘中寻找,如果找到了,则将磁盘中的缓存拷贝一份到内存中,然后使用缓存来设置视图。如果在
磁盘和内存中都没有找到,那么才会下载远程图片,然后将远程图片缓存在内存中或者是内存和磁盘上。

总结

本文对SDWebImage 的源代码进行了分析,其中着重介绍了它的缓存策略。因为篇幅有限,所以对于它的其他方面,如GCD 和NSOperationQueue 的使用,网络请求等知识都没有进行深入研究。在日后的系列文章中,我将回过头来针对这些知识进行再次探索,期待有新的收获。

这一段时间接连分析了MasonrySDWebImage 两个著名开源库的源代码。在我的心目中,SDWebImage 才是好代码的典范:架构漂亮,逻辑清晰,注释明确。

发表评论

电子邮件地址不会被公开。 必填项已用*标注