iOS底层之cache_t的探索

前言

对于iOS开发这来说,我们都了解了iOS对象的本质是一个结构体,在这个结构体当中包含了isa,superclass,cache,bits等一系列内容;在前边我们已经探索过了isa,superclass,和bits的作用,今天我们再一块儿探索一下cache的作用到底是什么?

一:cache是什么?

首先我们先通过对象的底层源码了解一下cache的基本信息;

image.gif

从上图我们可以看出来,在对象的底层的结构体中,cache的类型是一个名为cache_t的结构体类型;同时我们也可以看的出来,要想得到cache,我们可以通过生成的对象的内存地址便宜16位即可得到cache。如下图:

image.gif

从上图我们可以简单的了解到,cache_t在底层是以结构体的方式存在的,从字面意思我们大概可以了解到cache_t的作用是缓存的意思,既然是缓存那么肯定有插入,删除等操作,那么他是如何做的呢,我们从cache_t的源码结构中可以发现下图的代码:

image.gif

在bucket_t的这个结构体中我们看到了_imp,_sel这两个属性,是不是瞬间明白了它是干什么的了呢?

此时我们就大概的了解到了cache_t的数据结构是这样的:

image.gif

二:cache_t验证

接下来我们通过代码一块儿验证一下,cache_t的结构是否真的是这样的呢? 首先我们对上边获取到的数据进行一一打印

扫描二维码关注公众号,回复: 14188979 查看本文章

image.gif

从打印结果我们可以看出,我们无法获取里边的内容,以此推断出,那里边或许并没有我们想要的内容。我们接下来该如何操作呢?

我们功过对cache_t的源码分析可以看到下边的内容:

image.gif

在源码中有定义一个buckets的结构体指针,或许这个就是我们想要的呢:

image.gif

通过lldb打印我们并没有获取到什么有价值的内容,为什么会这样呢?经过分析得知,cache_t缓存的是我们调用过的方法,但是上边我们调试的时候并没有调用方法;接下来我们看一下调用过方法以后的数据是什么样的;

image.gif

面对这个答案一脸懵逼,明明是调用过这个方法了,为什么还是没有我们想要的呢?接着分析,buckets从字面上看里边应该是不是一个元素,buckets里边存储元素按照hash函数计算原则得到,hash不从0开始,数组才从0开始:于是我们有了一下结果

image.gif

此时我们在通过p $15.sel()打印就得到了下边的我们想要的结果了:

image.gif

那么我们如何获取saySomething的imp呢?

image.gif

从bucket_t的源码中我们可以看到,要想获取imp需要传入两个参数,一个bucket_t类型的base,另一个是Class类型的cls参数,于是我们就得到了下边的结果:

image.gif

终于看到了我们想看到内容了; 以上内容都是我们在有源码的情况下进行探索的,那么如果我们没有源码的时候还能得到我们想要的内容吗?当然可以,你只需要这么做:

image.gif

通过上边的方式,按照系统要求把该要的结构体自定义出来,然后在代码中调用一样能得到我们想要的内容;

三:写在最后

最后再说一下cache_t在缓存方法的时候的内存分配的规则吧,cache_t在分配内存的时候按照3/4扩容原则,也就是说cache_t最开始会分配一定数量的内容,当调用方法的时候会对方法进行缓存,当缓存的数量大于3/4的时候,会对当前内存进行2被扩容;具体探究方法下次在说吧!

猜你喜欢

转载自juejin.im/post/6981271210990501902