概念总成
image.png从图上能看出,SD主要分为4个主要模块:
SDWebImageManager(管理类)
这个是SD的核心类,用于分配任务与管理任务。他一般不会直接参与任务的执行。而是把任务下发到下面两个功能类来做。体现了谁的任务谁做的解耦合思想。
SDImageCache(缓存类)
,这个类用于图片缓存。
SDImageLoader,这个类用于图片加载(主要是下载功能的核心SDWebImageDownloade(下载类)
)
UIkit的拓展
。一般使用都是经过这些拓展来使用。方便直接使用SD的功能。
在通过UIkit的拓展类使用SD加载图片的时候,其实是通过SDWebImageManager类调用了缓存类与下载类经过一定的流程,才把图片显示到手机上。下面就图片显示简单的描述一下这个流程:
image.png
上图的标红已经把大致的流程显示的很清楚。下面详细的描述一下图片的下载流程:
1.首先,无论是下载还是缓存,都分别代表一个任务。
2.SD在执行下载或者缓存的时候,都会把对应的任务封装到operation里:
image.png
3.SD会把所有的operation封装到一个全局的字典里,这个字段是、NSMapTable
类
4.在所有操作之前,先判断这个View上有没有在执行任务,如果有就把即将开始的任务取消。没有就下一步
image.png
5.SD会调用管理类的loadImage方法。有缓存就加载缓存,没缓存就网络获取。
image.png
6.当图片已经下载完毕或者从缓存中获取到之后,有两个选择:
- 1.如果不需要设置图片就直接把图片返回给当前的View,并标记当前界面需要刷新。这样runloop在下一个周期,就会刷新view显示图片。
-
2.如果需要设置这张图片(如加水印之类的处理)则把图片通过block返回给当前方法的调用者,并在调用者处理完成图片之后,再标记刷新。
image.png
SD的缓存原理
SD的缓存由内存缓存和磁盘缓存同时控制。
1.配置类:
shouldDecompressImages
这个属性负责图片的压缩,但压缩的过程会消耗一定的内存,如果图片过大,会造成内存爆针。这时候只要把这个属性置位不压缩就可以了。
image.png
shouldCacheImagesInmemory
这个值是是否需要在内存中做缓存。
-
缓存类:
image.png
2.1 内存缓存:
缓存类首先定义了一个内存缓存的对象。
image.png
这是一个继承于系统的NSCache的对象,并定义了一个NSMapTable的弱缓存表。
与Dictionary相比,NSMapTable拥有更多的内存语义,如copy,assign,strong。
在用dic使用一个类(如NSString)作为key进行setValueForKey的时候,这个类就必须要实现NScopy协议。但是实现了这个协议后在setValueForKey时,dic会默认把这个类copy一下。这就会导致我们实际存储的东西跟想要存储的东西,不是同一个内存地址,也就是不是同一个东西。
而NSMapTable拥有更多的内存语义。如weak,strong。可以参见SD对于这个weakCache的初始化:
可以看到,这个NSMapTable把key做了强引用,把value做了弱引用。好处就是,因为不再有nscopy协议,保证这个了key,value一定就是我们传过来的key,value。而且还因为放在了全局的弱引用表中,当我们的对象(这个对象表示图片的内存)被释放之后,这个value也会就会被NSMapTable释放,而且由于value释放了,key也会被释放。这样既不用担心key不是我们想要存储的key,也不会担心key或者value无法释放。最主要的是,使用了NSMapTable后,
就能手动管理要存储的图片内存的释放
。}
不使用系统Cache因为NSCahe类的缓存释放时间完全由系统管理,我们无法得知NSCahe缓存的释放时间,这样可能在我们需要这个缓存的时候,系统却已经把这个缓存给释放了。所以SD继承于NSCahe后,又重新做了一个若缓存表,用来在需要某个缓存的时候,确定这个缓存不会被释放。
缓存机制的实现
-
SDMemoryCache重写了NSCache的核心方法:
image.png
虽然重写,但是还是调用super方法。通过NSCache的声明文件可发现。NSCache一定在实现的时候有个key与value对应的表没有暴露出来(这个表可能是字段也可能是数组,但是一定存在。因为如果不存在就不可能存的住keyvalue)。这就意味着系统的管理缓存释放,其实就是对这个表的管理。所以在给NSMapTable做setKeyvalue操作的时候,我们先把数据存一份在系统管理的表中,再存一份到我们自己管理的表中(就是刚才的weakCache)。但是这样做却多占用了一份内存。
image.png
-
-
- 取值方法步骤分为3步:
1.直接在系统的NSCache里查找,因为NSCache的释放完全由系统管理,所以取值的时候很可能value已经被系统释放。
2.如果已经被系统释放,就先从我们自己建的表中取出这个value,这样保证了每次取value的时候,就算NSCache的那一份已经释放,自己存的还能拿出来用。
3.如果取出了value,首先要再调用一次NSCache的存储,把这个value存到NSCache中。保证了NSCache中尽可能的拥有这份value,这样在下次再取值的时候,如果NSCache中的value没有被释放,就能直接拿来用。
- 取值方法步骤分为3步:
不难看出多占用一份内存的好处,用内存空间换取了查询时间。保证了尽量高效查询的同时,又保证了数据一定不被释放。
-
SD还监听了didReceiveMemoryWarning方法
image.png
一旦出现内存警告,SD会立即释放已占用的内存。保证了就算SD存储了两份内存,当出现内存警告时,不会造成APP闪退。
-
2.2 磁盘缓存
磁盘缓存,首先会创建一个缓存目录
然后把文件的key值进行MD5加密,再经过一些组合,最终得到文件名:
image.png
SD两种缓存的结合
image.png首先执行缓存操作的,是我们的管理类。执行方法是
loadImageWithURL
。内部如下,核心标红
image.png
5个重点。无论是存磁盘还是存硬盘,存的Data都是二进制数据。
标点1:是从缓存中查找图片。
标点2:因为从磁盘中查找比较耗时,为了确定操作安全,新建一个opration对象,并把加入全局的字段中,如果发生一个空间在图片未加载完全时就又加载了一次这张图片,则直接需要再次加载的操作。
标点3:自动释放池,当一个快代码会产生大量临时变量时,为了能让这些临时变量用完立马就释放,就需要给这些变量加入自动释放池中。又因为整个APP代码就是在一个自动释放池中,这一块就算不加这个释放池也没关系。但是释放的时机会慢不少,无法最快速度的回收内存。
标点4:根据key找出2进制数据。
标点5:拿到数据后,再存一份到内存中。这样下次再查的时候,就能最快的查找到。
网友评论