在iOS类的底层探索(上)中,我们介绍了类中有个class_rw_t结构体,属性、实例方法等都存放在这里,那么,成员变量又是放在什么地方的呢?
我们找到这个class_rw_t(以下简称rw)结构体,看到其中包含一个class_ro_t(以下简称ro),这就是存放成员变量的地方。
我们来验证一下: 通过命令行,我们获得的$6就是rw结构体,通过ro()方法获取ro结构体,其中包含ivars:
最终我们获取到一个ivar_list_t的结构体,这其中包含的就是成员变量信息,count为成员变量的数量。
到底是不是这样呢?我们来看看这里具体有哪些成员变量。
它继承自entsize_list_tt,使用get(uint32_t i)方法获取。
正好对应上该类的5个成员变量!
rw和ro的关系
ro是编译时生成的。
rw是运行时生成的,类一经使用,ro会被剪切到rw中。
ro存放了类的所有的成员变量,它是只读的,但是runtime又可以动态添加属性和方法,那这不矛盾了吗?
实际上,对于需要修改属性和方法的类,其rw中的ro会被拷贝一份,产生一个新的结构体class_rw_ext_t(以下简称rwe),它是可修改的,其实rwe不是完全拷贝ro,因为ro中还包含着非属性的成员变量,这些成员变量是不能被修改的,它们就不在被拷贝的行列中,像属性和方法是可以被修改的,就被会拷贝。
在iOS类的底层探索(上)中,我们说到了method()获取实例方法,方法是从rw中获取的,其实说得并不详细,再细看下源码逻辑:如果rwe存在,就返回rwe中的方法,反之则返回ro的baseMethods:
类方法存在哪里呢?
我们像查找对象的实例方法一样来查找类方法,先找到元类,再找到其class_rw_t,再走methods(),可找到类方法。
也就是说,类方法存于元类中,就像实例方法存于类中一样。
苹果为什么要设计元类这个东西呢,难道就是为了存放类方法吗?
其实,之所以设计元类,就是为了让类和元类的关系和对象和类的关系一样,其最终目的是为了复用消息机制。我们知道,让对象调一个实例方法,其实是给对象发送了一个消息,走的是objc_msgSend(消息接受者, 消息方法名);而调用类方法的时候,其实就是给类这个对象也发送了一个消息,其流程和实例方法一样。