美文网首页ios收藏
SDWebImage原理

SDWebImage原理

作者: __拼搏__ | 来源:发表于2019-06-20 00:37 被阅读0次

    先放上github的总概念图的划分

    上图是目前版本5.0.6的总览图的部分截图。

    有上图可以看出,SD主要分为4个主要模块:

    1、SDWebImageManager(管理类) 这个是SD的核心类,用于分配任务与管理任务。他一般不会直接参与任务的执行。而是把任务下发到下面两个功能类来做。充分体现了谁的任务谁做的解耦合思想。

    2、SDImageCache(缓存类),这个类用于图片缓存。

    3、SDImageLoader,这个类用于图片加载(主要是下载功能的核心SDWebImageDownloade(下载类))

    4、UIkit的拓展。一般使用都是经过这些拓展来使用。方便直接使用SD的功能。

    在通过UIkit的拓展类使用SD加载图片的时候,其实是通过SDWebImageManager类调用了缓存类与下载类经过一定的流程,才把图片显示到手机上。下面就图片显示简单的描述一下这个流程:

    上图的标红已经把大致的流程显示的很清楚了。下面详细的描述一下图片的下载流程:

    1.首先,无论是下载还是缓存,都分别代表一个任务。

    2.SD在执行下载或者缓存的时候,都会把对应的任务封装到operation里如下图:

    3.SD会把所有的operation封装到一个全局的字典里,这个字段是NSMa'pTable类

    4.在所有操作之前,先判断这个View上有没有在执行任务,如果有就把即将开始的任务取消。没有就下一步

    5.SD会调用管理类的loadImage方法。有缓存就加载缓存,没缓存就网络获取。(这一步下来再细聊)

    6.当图片已经下载完毕或者从缓存中获取到之后,有两个选择:1.如果不需要设置图片就直接把图片返回给当前的View,并标记当前界面需要刷新。这样runloop在下一个周期,就会刷新view显示图片。2.如果需要设置这张图片(如加水印之类的处理)则把图片通过block返回给当前方法的调用者,并在调用者处理完成图片之后,再标记刷新。

    下面聊聊SD的缓存原理:

    SD的缓存由内存缓存和磁盘缓存同时控制。

    1.先看看配置类:下图是压缩属性:

    属性的注释已经很清楚了。这个属性负责图片的压缩。但压缩的过程会消耗一定的内存,如果图片过大,会造成内存爆针。这时候只要把这个属性置位不压缩就可以了。

    下图是是否需要在内存中做缓存。

    2.再看缓存类:

    1.先了解一下内存缓存:

    缓存类首先定义了一个内存缓存的对象。我们看一下这个对象:

    这是一个继承于系统的NSCache的对象,并定义了一个NSMapTable的弱缓存表。

    {NSMapTable与dic(语义:nscopy)有什么区别?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后,我们就能手动管理要存储的图片内存的释放。}

    那为什么系统有了用来处理内存缓存的NSCahe类,SD还要再继承这个类呢?

    原因是因为NSCahe类的缓存释放时间完全由系统管理,我们无法得知NSCahe缓存的释放时间,这样可能在我们需要这个缓存的时候,系统却已经把这个缓存给释放了。所以SD继承于NSCahe后,又重新做了一个若缓存表,用来在需要某个缓存的时候,确定这个缓存不会被释放。

    那这个机制具体是怎么实现的呢?

    首先SDMemoryCache重写了NSCache的核心方法如:

    既然重写,但是还是调用super方法。通过观察NSCache的声明文件不难发现。NSCache一定在实现的时候有个key与value对应的表没有暴露出来(这个表可能是字段也可能是数组,但是一定存在。因为如果不存在就不可能存的住keyvalue)。这就意味着系统的管理缓存释放,其实就是对这个表的管理。所以在给NSMapTable做setKeyvalue操作的时候,我们先把数据存一份在系统管理的表中,再存一份到我们自己管理的表中(就是刚才的weakCache)。但是这样做却多占用了一份内存。那究竟好处在哪,我们看一下取值方法:

    取值方法步骤分为3步。第一步直接在系统的NSCache里查找,因为NSCache的释放完全由系统管理,所以取值的时候很可能value已经被系统释放了。第二步:如果已经被系统释放了,我们就先从我们自己建的表中取出这个value,这样保证了每次取value的时候,就算NSCache的那一份已经释放了,我们自己存的还能拿出来用。第三步:如果我们取出了value,首先要再调用一次NSCache的存储,把这个value存到NSCache中。保证了NSCache中尽可能的拥有这份value,这样在下次再取值的时候,如果NSCache中的value没有被释放,我们就能直接拿来用。

    这就不难看出多占用一份内存的好处,我们是用内存空间换取了查询时间。保证了尽量高效查询的同时,又保证了数据一定不被释放。(这个思维方式真的屌)。

    而且SD还监听了didReceiveMemoryWarning方法:

    一旦出现内存警告,SD会立即释放已占用的内存。保证了就算SD存储了两份内存,当出现内存警告时,不会造成APP闪退。

    我们再来看看磁盘缓存:

    磁盘缓存,首先会创建一个缓存目录

    然后把文件的key值进行MD5加密,再经过一些组合,最终得到文件名:

    了解了内存缓存与磁盘缓存后,我们再来盘点一下,SD究竟怎么把这两种缓存结合到一起的。

    首先执行缓存操作的,一定是我们的管理类。执行方法就是loadImageWithURL。然后我们找到该方法中的查询语句,点进去,看实现

    实现如下,我把核心的东西,标红了

    图中5个点比较重要。再讲解5个点之前,我们应该知道无论是存磁盘还是存硬盘,存的都是二进制数据。

    其中标点1:是从缓存中查找图片。

    标点2:因为从磁盘中查找比较耗时,为了确定操作安全,新建一个opration对象,并把加入全局的字段中,如果发生一个空间在图片未加载完全时就又加载了一次这张图片,则直接需要再次加载的操作。这就跟文章开头提的对应上了。

    标点3:自动释放池,当一个快代码会产生大量临时变量时,为了能让这些临时变量用完立马就释放,就需要给这些变量加入自动释放池中。又因为整个APP代码就是在一个自动释放池中,这一块就算不加这个释放池也没关系。但是释放的时机会慢不少,无法最快速度的回收内存。

    标点4:根据key找出2进制数据。

    标点5:拿到数据后,再存一份到内存中。这样下次再查的时候,就能最快的查找到。

    相关文章

      网友评论

        本文标题:SDWebImage原理

        本文链接:https://www.haomeiwen.com/subject/oyvlqctx.html