每天都在用的 SDWebImage, 你了解它的缓存策略吗?
Posted SwiftCafe
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了每天都在用的 SDWebImage, 你了解它的缓存策略吗?相关的知识,希望对你有一定的参考价值。
闲言少叙,咱们这就开始。 首先咱们来看看 SDWebImage 的整体结构:
有一个专门的 Cache 分类用来处理图片的缓存。 这里面也有两个类 SDImageCache 和 SDImageCacheConfig。 大部分的缓存处理都在 SDImageCache 这个类中实现。其他几个文件夹咱们分别有个字的功能,因为咱们这次专门讨论缓存策略,所以其他内容暂时略过。
首先,SDWebImage 的图片缓存采用的是 Memory 和 Disk 双重 Cache 机制, 听起来挺高大上吧。其实也不复杂。
我们先来看看 Memory Cache,贴一段 SDImageCache 的代码:
|
这里我们发现, 有一个叫做 memCache 的属性,它是一个 NSCache 对象,用于实现我们对图片的 Memory Cache。 SDWebImage 还专门实现了一个叫做 AutoPurgeCache 的类, 相比于普通的 NSCache, 它提供了一个在内存紧张时候释放缓存的能力:
|
其实就是接受系统的内存警告通知,然后清除掉自身的图片缓存。 这里大家比较少见的一个类应该是 NSCache 了。 简单来说,它是一个类似于 NSDictionary 的集合类,用于在内存中存储我们要缓存的数据。详细信息大家可以参考官方文档:。
说完 Memory Cache, 我们再来说说 Disk Cache,也就是文件缓存。
SDWebImage 会将图片存放到 NSCachesDirectory 目录中:
|
然后为每一个缓存文件生成一个 md5 文件名, 存放到文件中。
为了节约篇幅,提升大家的阅读体验,这里尽量少贴大段代码。 我们前面介绍了 SDWebImage 同时使用内存和硬盘两种缓存。 那么我们来看看当使用 SDWebImage 读取图片时候的完整流程。 我们一般会使用 SDWebImage 对 UIKit 的扩展,直接加载图片:
|
首先这个 Category 方法 sd_setImageWithURL 内部会调用 SDWebImageManager 的 downloadImageWithURL 方法来处理这个图片 URL:
|
SDWebImageManager 内部的 downloadImageWithURL 方法会先使用我们前面提到的 SDImageCache 类的 queryDiskCacheForKey 方法,查询图片缓存:
|
再来看 queryDiskCacheForKey 方法内部, 先会查询 Memory Cache :
|
如果 Memory Cache 查找不到, 就会查询 Disk Cache:
|
查询 Disk Cache 的时候有一个小插曲,就是如果 Disk Cache 查询成功,还会把得到的图片再次设置到 Memory Cache 中。 这样做可以最大化那些高频率展现图片的效率。
如果缓存查询成功, 那么就会直接返回缓存数据。 如果不成功,接下来就开始请求网络:
|
|
为什么要有这个 failedURLs 呢, 因为 SDWebImage 默认会有一个对上次加载失败的图片拒绝再次加载的机制。 也就是说,一张图片在本次会话加载失败了,如果再次加载就会直接拒绝。SDWebImage 这样做可能是为了提高性能吧。这个机制可能会容易被大家忽略,所以这里特意提一下,说不定哪天遇到一些奇怪问题时候,这个知识点会帮你快速定位问题~
如果下载图片成功了,接下来就会使用 [self.imageCache storeImage] 方法将它写入缓存,并且调用 completedBlock 告诉前端显示图片:
|
好了,到此为止 SDWebImage 的整体图片加载流程就都走完了。 由于要控制篇幅,我这里只挑了最重点的几个节点写出来,SDWebImage 的完整机制肯定会更全面,先帮大家疏通思路。
SDWebImage 的整体图片处理流程咱们体验了一遍。 那么有哪些重要的点对咱们使用它会有帮助呢? 我帮大家整理了一些。
你可以在加载图片的时候设置 SDWebImageRetryFailed 标记,这样 SDWebImage 就会加载之前失败过的图片了。 记得我们前面提到的 failedURLs 属性了吧,这个属性是在内存中存储的,如果图片加载失败, SDWebImage 会在本次 APP 会话中都不再重试这张图片了。当然这个加载失败是有条件的,如果是超时失败,不记在内。
总之,如果你更需要图片的可用性,而不是这一点点的性能优化,那么你就可以带上 SDWebImageRetryFailed 标记:
|
SDWebImage 会在每次 APP 结束的时候执行清理任务。 清理缓存的规则分两步进行。 第一步先清除掉过期的缓存文件。 如果清除掉过期的缓存之后,空间还不够。 那么就继续按文件时间从早到晚排序,先清除最早的缓存文件,直到剩余空间达到要求。
具体点,SDWebImage 是怎么控制哪些缓存过期,以及剩余空间多少才够呢? 通过两个属性:
|
maxCacheAge 是文件缓存的时长, SDWebImage 会注册两个通知:
|
分别在应用进入后台和结束的时候,遍历所有的缓存文件,如果缓存文件超过 maxCacheAge 中指定的时长,就会被删除掉。
同样的, maxCacheSize 控制 SDImageCache 所允许的最大缓存空间。 如果清理完过期文件后缓存空间依然没达到 maxCacheSize 的要求, 那么就会继续清理旧文件,直到缓存空间达到要求为止。
了解了这个机制对我们有什么帮助呢? 我们来继续讲解,我们平时在使用 SDWebImage 的时候是没接触过它们的。那么以此推理,它们一定有默认值,也确实有:
|
上面是 maxCacheAge 的默认值,注释上写的很清楚,缓存一周。 再来看看 maxCacheSize。 翻了一遍 SDWebImage 的代码,并没有对 maxCacheSize 设置默认值。 这就意味着 SDWebImage 在默认情况下不会对缓存空间设限制。
这一点可以在 SDWebImage 清理缓存的代码中求证:
|
说明一下, 上面代码中的 currentCacheSize 变量代表当前图片缓存占用的空间。 从这里可以看出, 只有在 maxCacheSize 大于 0 并且当前缓存空间大于 maxCacheSize 的时候才进行第二步的缓存清理。
这意味着什么呢? 其实就是 SDWebImage 在默认情况下是不对我们的缓存大小设限制的,理论上,APP 中的图片缓存可以占满整个设备。
SDWebImage 的这个特性还是比较容易被大家忽略的,如果你开发的类似信息流的 APP,应该会加载大量的图片,如果这时候按照默认机制,缓存尺寸是没有限制的,并且默认的缓存周期是一周。 就很容易造成应用存储空间占用偏大的问题。
那么可能有人会说了,现在 iPhone 的存储空间都很大,多缓存一点也不是问题吧? 但你是否知道 ios 上还有一个用量查询的功能呢。在设置项中用户可以查看每个 APP 的空间使用情况, 如果你的 APP 占用空间比较大的话,就很容易成为用户的卸载目标,这应该是需要关注的一个细节。
另外,过多的占用缓存空间其实并不一定有用。大部分情况是一些图片被缓存下来后,很少再被重复展现。所以合理的规划缓存空间尺寸还是很有必要的。可以这样设置:
|
maxCacheSize 是以字节来表示的,我们上面的计算代表 50M 的最大缓存空间。 把这行代码写在你的 APP 启动的时候,这样 SDWebImage 在清理缓存的时候,就会清理多余的缓存文件了。
这次跟大家聊了聊 SDWebImage 整体流程,以及它的缓存策略。SDWebImage 是一个历时很久的开源库,并且它不断的保持着更新。 虽然是一个并不算很复杂的开源库,但仔细研读一下它的代码, 会发现它的内部很多机制设计的还是很巧妙的。 为了保证大家的阅读体验,尽量控制文章的篇幅,这里尽量选出对大家最有帮助的内容和大家分享。这篇文章结构整理花了几天时间,仔细筛选最重要的内容。想达到的效果就是,让他读起来不累,但却能很快抓住重点,让大家得到有用的信息。也希望大家对阅读体验能够提出反馈,帮助我给大家创造更好的内容。
如果想了解 SDWebImage 更详细的内容,那么它的 Github 主页就是最好的地方了,在这里也贴出来,供大家参考: 。
以上是关于每天都在用的 SDWebImage, 你了解它的缓存策略吗?的主要内容,如果未能解决你的问题,请参考以下文章
原来不只是fastjson,这个你每天都在用的类库也被爆过反序列化漏洞!