YYKit源码学习——YYMemoryCache

Posted 人生如梦91

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了YYKit源码学习——YYMemoryCache相关的知识,希望对你有一定的参考价值。

YYKit是在项目开发中经常使用的一个库,我很膜拜作者,很惭愧,同样身为90后,差距实在太大了,别人这么牛逼,我却如此菜鸡,我也知道,到了一定程度,人就会遇到技术瓶颈,对于我,提高自己水平的惟一方法就是阅读源代码,我也一直很想这样学习,下载了很多源代码,但却一直无法静下心来学习,看到那长篇大幅的英文字母就头痛,今日公司无事,于是又瞥见桌面上的YYKit文件夹,于是打开了看一看,挑选了代码比较简单的YYMemoryCache进行阅读,并将自己的想法记录下来,方便以后查阅

_YYLinkedMap

YYMemoryCache的内部包含了一个CFMutableDictionary字典,其内部结构如下所示:

其中,CFMutableDictionaryRef用于存储缓存数据,_totalCost用于记录总消费,_totalCount用于记录总数量, _releaseOnMainThread用于记录是否在主线程上释放,_releaseAsynchronously用于记录是否异步释放数据。

另外,还有两个_YYLinkedMapNode*类型的_head和_tail的结点,__YYLinkedMap还包含了一个双向链表,用于实现LRU缓存淘汰算法,这个算法相对比较简单,简单来说,就是用一个链表记录,最近使用的在表头,最近未使用的在表尾,从尾到头逐步删除过期数据。

其中实现的方法如下图所示,根据方法名很容易知道方法的作用

到此可以知道,_YYLinkedMap是一个包含CFMutableDictionary和一个双向链表的结构,其中CFMutableDictionary用于缓存的查找,而双向链表则用于数据的删除。

YYMemoryCache

在YYMemoryCahce的init方法,主要是初始化一些数据和添加UIApplicationDidReceiveMemoryWarningNotification和UIApplicationDidEnterBackgroundNotification两个通知,但在最后,调用了一个叫_trimRecursively的方法,这个方法的实现如下所示:

这个方法的作用是根据设置的_autoTrimInterval,不断的开辟子线程删除超过限制的数据,其中_trimInBackground方法的实现如下:

在指定的线程中删除超过限制的数据,不过其实我不明白为什么要这么做,既然已经开辟了一个子线程,却为什么又要到指定的线程中去做删除操作,根据定义_queue并不是并行队列而是串行队列。

_trimToCost、_trimToCount、_trimToAge的实现类似,其中一个实现如下:

其中的代码都好理解,只有最后一段,实现是看不懂,后来在YYKit作者的博客中找到解答,count只是一个随意调用的方法,这样做的目的是让block持有holder,令holder能在子线程中释放,从而提高性能。看到这里,我实在不得不佩服作者,谅我等屌丝根本不可能想到还有这样的操作。

还有一处,在227行,作者为什么不使用while一次性释放掉所有超过限制的数据,而是使用了if,每次都去尝试加锁呢?我想这应该是为了保证使用的流畅,因为假如不合条件的数据很多,而此地若使用while便会阻塞读写操作,影响使用,不知道自己的猜想对不对。

最后就是关键的添加和查找操作了,查找的代码如下所示:

如果查找到数据,即缓存命中,这个缓存数据很可能在下次仍然会被使用,根据LRU淘汰算法,更新结点的使用时间,将其移动到表头。

插入数据的部分代码如下所示:

代码都很简单,也没什么好说的,有一点,既然已经有一个递归删除不合格的方法_trimRecusively方法存在,为什么这里还要执行一次删除操作呢?直接等待递归时释放不行么,这样做岂不是又延迟了锁的释放时机?

最后还是再得感叹一下,虽然我也很早就接触到了YYKit这个库,但一直没有去读过里面的任何代码,直到17年才阅读了里面的一小部分代码,但并没有任何文档记录,导致现在几乎忘的一干二净。此次若时间充足,必定要好好研究学习一番。

以上是关于YYKit源码学习——YYMemoryCache的主要内容,如果未能解决你的问题,请参考以下文章

YYKit源码探究(五十六) —— NSData分类之Hash(一)

YYKit之YYText

专访 YYKit 作者 ibireme: 开源大牛是怎样炼成的

YYKit笔记之FPS

C++学习记录:一个小线程池的源码分析

C++学习记录:一个小线程池的源码分析