本文由快学吧个人写作,以任何形式转载请表明原文出处。
此篇与第六章、第七章和第八章为关联章节。所用资料和这三篇相同。
一、准备项目
在第六第七和第八章准备的项目的基础上,为类添加方法和类方法及其实现。
- 创建一个JDPerson实例person,并获取它的类pClass,并在项目中打好断点。
二、类的方法存储(在rw中)
- 运行项目,然后在lldb中打印pClass的内存地址,并且将其首地址平移32位,获取到bits的内存地址。(原因看第6,7,8章),并将类型强转为bits的类型
class_data_bits_t。
- 上图中的
$2即为一个class_data_bits_t类型的指针,可以调用class_data_bits_t结构体内的data()方法,从而获取JDPerson类的data信息。
- 得到了类型为
class_rw_t类型的$3,查看class_rw_t的源码,找到有methods()方法可以获取类的方法信息,用$3调用methods()方法。
源码 :
调用 :
- 进入list,然后进入ptr
- 得到一个
method_list_t类型的指针,取指针中的内容
- 没有具体的方法信息,那么看
method_list_t是否有方法可以获取具体的方法信息。查找method_list_t源码。
- 没有发现获取具体信息的函数,但是发现
method_list_t继承于entsize_list_tt,所以查看entsize_list_tt源码.
- 找到get函数,父类的函数,子类也可以用,所以
method_list_t也可以用get()函数,那么让method_list_t类型的$7调用get()函数。
- 在第5步的图中可以得到信息count=4,也就是有4个方法,在第8步中得到信息可以用
get()函数获取方法信息,但是从0取到3,都没有任何的信息,获得的是method_t类型的对象,所以查看一下method_t是什么。
- 只要获取到SEL、types和imp,就可以查看存储在methods的list的ptr里面的方法的具体信息,所以继续找
method_t源码,看如何获取到big结构体。
- 找到
big()函数,那么就可以让method_t类型的对象 :$8、$9、$10、$11调用big()函数,获取到方法的具体信息。
上图中找到了实例方法zhuanQian,找到了属性tiZhong的set和get方法,最后一个方法叫.cxx_destruct,imp在JDPerson.m的第10行。
.cxx_destruct:
这是在ARC模式下用于释放成员变量的方法,调用的时机是在dealloc方法调用的时候再调用。只有当前的类拥有实例变量的时候,类的信息中才会出现这个方法,属性也会在编译后变成实例变量,所以类中有属性也会有这个方法。
父类的实例变量不会使子类自带这个方法。
三、类的方法的存储(在ro中)
上一节中说过class_rw_t和class_ro_t都会存储类的方法,上面已经看过实例方法在rw中的存储了,下面看一下ro中是否也有实例方法的存储。
步骤类似,说的太多了,就直接上图了
方法并不在baseMethodList里面。那么就需要找class_ro_t的源码了
图中的方法是baseMethods。所以需要class_ro_t类型的对象$5调用baseMethods函数。
直接取指针指向的内容
这就和上面一样了,看源码,找method_list_t,发现是继承于entsize_list_tt的,而entsize_list_tt源码中有一个get()函数。所以method_list_t类型的$9可以调用它的父类的get()函数。
又是method_t,上面过了,这里就不说了。直接调用big()函数。
结论 : 类的实例方法也是存在于
class_ro_t中的。也就是既存在于class_rw_t中也存在于class_ro_t中。
四、类方法的存储
这个就不多说了,JDPerson是继承于NSObject的,但是NSObject中是没有我们的类方法的,因为类方法是我们自己在子类JDPerson中定义的,那么类方法在哪里?
思路 : runtime中有一个常用的API,也是我们项目中用到的API :
object_getClass获取对象的类信息。
1. object_getClass源码
但是object_getClass的参数只能是实例对象,虽然说万物皆对象,但是直接将JDPerson填入肯定是会报错的。所以只能查看object_getClass的源码,看看可不可以利用它的实现原理,来找到JDPerson继承的类的信息。
object_getClass的源码如下 :
找getIsa() :
很明显我们不是isTaggedPointer,所以会直接走return ISA(),因为isTaggedPointer带个非!。
进入ISA() :
进入getDecodedClass :
mac环境下,SUPPORT_INDEXED_ISA为0,所以直接进getClass :
如图中备注所示。
返回的是clsbits,而clsbits是isa的结构体成员bits来进行的赋值。关于isa可以看第四章。
所以,其实父类的信息就是从isa中拿到的,并且是对isa做了一个&的操作,是&上一个ISA_MASK。
看下ISA_MASK
mac环境是x86,上面还有arm64的,就不看了。
2. 取JDPerson的isa指向的类
- 先打印JDPerson的内存,然后用isa和
ISA_MASK做&,得到的就是JDPerson的isa指向的类。
- 打印这个JDPerson的isa指向的类的内存地址,然后平移32位,强转成
class_data_bits_t。
- 后续的步骤和上面一致,直接上图。
类方法找到,在JDPerson的isa指向的类的bits中,而JDPerson的isa指向的类,我们通常称其为元类。
结论 : 类方法在类的元类的
bits中存储。