类结构源码
我们先来看一下runtime中类的结构定义:
|
|
从上面的结构体定义可以看出,每一个类的第一个属性都会是一个isa指针,然后是super和cache,之后的bits里面就是类的结构,我们需要使用llbd命令Debug bits内部的内存分配情况,在开始探究之前,我们可以先熟悉下bits中包含的主要数据结构,如下:
|
|
可以看到,我们熟悉的函数列表,属性列表和协议列表都在其中,下面我们就通过内存堆栈的Debug来证明下上面的类结构。
知识储备
在开始探索前,我们先来看一下这张图:
这里有一个知识点我们需要了解,一个类(不考虑继承)在内存中会存在3种与它有关联的对象,分别是实例对象,类对象和元类对象,这3种对象通过isa指针进行关联,实例对象可能存在多个,类对象和元类对象是唯一的。其中类对象存储实例方法和属性,元类对象存储类方法。
开始探究
我们先来自定义一个简单的类,如下:
|
|
实例对象
在主函数中我们简单创建一个上面的model实例,然后就可以断点调试了,首先我们查看实例对象的内存地址:
|
|
根据对源码的分析,我们知道bits数据存储在首地址偏移32个字节的地方,so我们这样访问bits的地址
|
|
拿到bits的地址后,然后访问改结构体的data方法拿到class_rw_t数据
|
|
然后我们就可以访问该结构内部的相关方法获取数据了,比如方法列表
|
|
通过查看内存情况,发现方法列表内部的count为0,说明方法并未存放在实例对象中。
然后看看属性列表
|
|
属性列表访问失败,说明也是没有数据的。
类对象
然后我们再来看看类对象中的内存情况,这里我们先拿到类对象的首地址:
|
|
上面也提到过,类对象是唯一的,所以可以这样访问,其他步骤都差不多就不在重复粘贴,这里只放最后的结果
|
|
除了我们定义的实例方法外,还看到了属性的get和set方法,还有一个编译器默认添加的析构方法。接着是属性列表
|
|
只有一个我们定义的name属性。
元类对象
最后我们来看看元类的内存分配情况,首先我们拿到元类的首地址:
|
|
方法列表:
|
|
元类中存放了我们上面定义的一个类方法
属性列表:
|
|
元类中没有存放属性。
小结
通过以上的探索过程,证明了我们之前的结论其中类对象存储实例方法和属性,元类对象存储类方法。,通过源码+lldb相结合的方式,可以更好的证明我们已知的一些结论。也给我们探索更广阔的空间提供了可能。