前言
前面我们探究了alloc和对象,对象是由类alloc出来的,更确切的说是由alloc开辟空间绑定类的isa。所以对象源自于类,我们这篇文章就来探寻一下类。
一、isa
前面的探索中,一直离不开isa,首先上一张官方的isa走位和继承图
这张图中包含了继承链和
isa走位,接下来我们来一一验证
1、验证继承链
首先我们创建2个类LGPerson和LGDirver,LGPerson继承于NSObject,LGDirver继承于LGPerson,如图:
然后打印类和元类的superClass,如图:
结果
按照结果我们画个简易的继承图:
是不是好像在哪看过?上面的isa走位图的右边2列就是咯!只是名字没有改。
2、验证isa链
1、方法
要验证isa的走位我们需要通过isa获取到类的指针地址,通过object_getClass我们找到了
再进入
getIsa()
进入
ISA()
然后在
return (Class)(isa.bits & ISA_MASK);,isa和isa.bits是一样的,可以看我的另一篇文章传送门的结尾部分有提到。
所以获取到class的方法就是 isa & ISA_MASK,我们再来看看ISA_MASK
分为
arm64和x86_642种,我们跑的是mac(本人电脑非M1),所以看x86_64模式,所以ISA_MASK就是0x00007ffffffffff8,方法找清楚了我们开始干。
2、验证
我们创建实例并获取其类、元类、根元类、根根元类,打印各类及其地址,如图:
跑起来,结果如下:
这个看不出来什么,需要我们分析,用前面的方法进行分析,分析结果如图:
我们用
实例kc的isa获取到了类的地址、类的isa获取到了元类的地址、元类的isa获取到了根元类的地址、根元类的isa获取到了根根元类的地址,对比根元类和根根元类的地址发现是一样的,所以画个简易的isa走位图:
同理可得
LGPerson的isa走位图和NSObject的isa走位图都是一样的,那么合并起来,如图:
是不是和官方的
isa走位一样?这样就验证了isa的走位。
3、走位图
我们分别验证了继承链和isa的走位图,接下来我们把它们放在一起来还原官方的图:
二、类的结构
首先从源码中截取一段类的结构体:
这里有
3个成员变量,加上继承于objc_object,如图:
所以就是
4个成员变量:isa、superclass、cache、bits。
isa:结构体指针8字节;superclass: 结构体指针8字节;cache: 未知;bits:未知; 我们对未知的进行探究:
1、cache的大小
cache是cache_t类型,我们进去看一下:
如图,在64位环境中的标注,所以
cache的大小为 8 + 8 = 16。
2、bits
在探究bits前我们需要了解地址偏移,就是连续的内存的变量可以通过前一个变量的首地址+大小(偏移量)得到后一个变量的首地址,不多赘述。
我们已经知道类和前3个成员变量的大小了,所以bits的首地址我们已经算是知道了,留作备用。
1、源码查看
接下来,我们看一下class_data_bits_t类型,如图:
太长了,简单的处理一下,截个全图。我们发现只有一个
bits的成员,除开swift相关的代码,我们发现2个不一样的东西class_rw_t和class_ro_t,打开方法看一下:
class_ro_t通过class_rw_t获取的,所以我们只看class_rw_t,进入,代码太长,简单处理,如图:
从成员变量中我们看不到什么特别的东西,继续找方法,发现
method_array_t、property_array_t、protocol_array_t。
捋了一遍源码,还是没太多的头绪,接下来代码跑起来,我们进行lldb查看。
2、lldb调试
利用前面的方法和源码分析,我们用lldb来验证。创建LGPerson:
调用:
断点进入程序:
接下来直接开始调试,利用
p和ISA_MASK获取到LGPerson这个类,加深印象,如图:
此时获取到
LGPerson再继续查看内部:
根据前面的计算,取到
LGPerson的地址0x100004718,便宜计算得到bits的地址为0x100004738,强转打印出来:
调用
data()方法:
1、查看property_array_t:
获取
ptr:
然后开始获取所有的成员,注意
count = 8,所以只有8个:
对比一下发现和我们的
LGPerson是一样的。
2、查看method_array_t
有17个方法,我们获取部分,如图:
很奇怪,是空的,我们去
method_t中看一下:
发现方法存在
big里,再次尝试:
随便取了一些,发现除了第
4位是我们自定义的saySomething,其他的都是set和get方法,8个属性的set和get方法共16,加上saySomething所以是17个。
3、查看protocol_array_t
由于没有协议,所以查看不到。
3、简单总结
在类中含有4个成员变量:isa、superclass、cache、bits,其中bits里存储了属性,方法,协议等。