cache_t分析
一、初探cache_t
1.cache_t结构
首先找到objc_class
点击cache进去看看
太多了 就不都截图了 看看宏定义是什么回事
CACHE_MASK_STORAGE_HIGH_16 : 真机&&64位
CACHE_MASK_STORAGE_LOW_4 : 真机&&非64位
CACHE_MASK_STORAGE_OUTLINED:模拟器和Mac呗
bucket_t结构
cache_t中的_buckets、_mask、_occupied从字面意思上理解为桶、面具、占据,但是我们不知道这三个的作用是否与他们的名字有关系,下面我们先从LLDB打印一些信息来看看
2.lldb调试
代码准备
2.1开始调试
调用sayhello
我们可以看到想要的东西了(_buckets, _mask, _flags, _occupied)
内部有很多方法
通过调用buckets()查看结构
发现包含sel和imp 看一下源码方法
调用获取
2.2多个方法调试
发现 _occupied有变化,这个我们一会在分析,我们现在先了解 buckets
找不到saycode
那我大胆猜一下!buckets有s,那他是不是有多个?,会不会是个类似数组结构?那我通过指针偏移可不可以得到想要的东西?那验证一下呗!
搞定 现在我们了解了buckets里面存sel和imp了
二. cache_t实现原理分析
_occupied如何变化的?而且当方法增多,mask也改变。还有buckets怎么存的?
看到了incrementOccupied,让我们看看谁调用的
找到了insert 全局搜索 找到cache_fill
全局搜索cache_fill,发现在写入之前,还有一步操作,即cache读取,即查找sel-imp,
本文的重点还是分析cache存储的原理,接下来根据cache_t写入的流程图,着重分析insert方法insert方法分析
大致分为三步
第一步:计算出当前的缓存占用量
根据occupied的值计算出当前的缓存占用量,当属性未赋值及无方法调用时,此时的occupied()为0,而newOccupied为1,如上图所示
alloc申请空间时,此时的对象已经创建,如果再调用init方法,occupied也会+1
当有属性赋值时,会隐式调用set方法,occupied也会增加,即有几个属性赋值,occupied就会在原有的基础上加几个
当有方法调用时,occupied也会增加,即有几次调用,occupied就会在原有的基础上加几个
第二步:根据缓存占用量判断执行的操作
reallocate方法:开辟空间
该方法,在第一次创建以及两倍扩容时,都会使用,其源码实现如图所示
allocateBuckets方法:向系统申请开辟内存,即开辟bucket,此时的bucket只是一个临时变量
setBucketsAndMask方法:将临时的bucket存入缓存中,此时的存储分为两种情况:
如果是真机,根据bucket和mask的位置存储,并将occupied占用设置为0
如果不是真机,正常存储bucket和mask,并将occupied占用设置为0
如果有旧的buckets,需要清理之前的缓存,即调用cache_collect_free方法,其源码实现如下
该方法的实现主要有以下几步:
_garbage_make_room方法:创建垃圾回收空间
如果是第一次,需要分配回收空间
如果不是第一次,则将内存段加大,即原有内存*2
记录存储这次的bucket
cache_collect方法:垃圾回收,清理旧的bucket
第三步:针对需要存储的bucket进行内部imp和sel赋值
这部分主要是根据cache_hash方法,即哈希算法 ,计算sel-imp存储的哈希下标,分为以下三种情况: 如果哈希下标的位置未存储sel,即该下标位置获取sel等于0,此时将sel-imp存储进去,并将occupied占用大小加1 如果当前哈希下标存储的sel 等于 即将插入的sel,则直接返回 如果当前哈希下标存储的sel 不等于 即将插入的sel,则重新经过cache_next方法 即哈希冲突算法,重新进行哈希计算,得到新的下标,再去对比进行存储
cache_hash:哈希算法
cache_next:哈希冲突算法
最后将上述流程总结一个流程图
画图软件用得少 图不太好看 谅解哈~~