九、类的方法和类方法的存储

80 阅读4分钟

本文由快学吧个人写作,以任何形式转载请表明原文出处。

此篇与第六章第七章第八章为关联章节。所用资料和这三篇相同。

一、准备项目

在第六第七和第八章准备的项目的基础上,为类添加方法和类方法及其实现。

图片.png

图片.png

  1. 创建一个JDPerson实例person,并获取它的类pClass,并在项目中打好断点。

图片.png

二、类的方法存储(在rw中)

  1. 运行项目,然后在lldb中打印pClass的内存地址,并且将其首地址平移32位,获取到bits的内存地址。(原因看第6,7,8章),并将类型强转为bits的类型class_data_bits_t

图片.png

  1. 上图中的$2即为一个class_data_bits_t类型的指针,可以调用class_data_bits_t结构体内的data()方法,从而获取JDPerson类的data信息。

图片.png

  1. 得到了类型为class_rw_t类型的$3,查看class_rw_t的源码,找到有methods()方法可以获取类的方法信息,用$3调用methods()方法。

源码 :

图片.png

调用 :

图片.png

  1. 进入list,然后进入ptr

图片.png

  1. 得到一个method_list_t类型的指针,取指针中的内容

图片.png

  1. 没有具体的方法信息,那么看method_list_t是否有方法可以获取具体的方法信息。查找method_list_t源码。

图片.png

  1. 没有发现获取具体信息的函数,但是发现method_list_t继承于entsize_list_tt,所以查看entsize_list_tt源码.

图片.png

  1. 找到get函数,父类的函数,子类也可以用,所以method_list_t也可以用get()函数,那么让method_list_t类型的$7调用get()函数。

图片.png

  1. 在第5步的图中可以得到信息count=4,也就是有4个方法,在第8步中得到信息可以用get()函数获取方法信息,但是从0取到3,都没有任何的信息,获得的是method_t类型的对象,所以查看一下method_t是什么。

图片.png

  1. 只要获取到SEL、types和imp,就可以查看存储在methods的list的ptr里面的方法的具体信息,所以继续找method_t源码,看如何获取到big结构体。

图片.png

  1. 找到big()函数,那么就可以让method_t类型的对象 : $8$9$10$11调用big()函数,获取到方法的具体信息。

图片.png

上图中找到了实例方法zhuanQian,找到了属性tiZhong的set和get方法,最后一个方法叫.cxx_destruct,imp在JDPerson.m的第10行。

.cxx_destruct :

  1. 这是在ARC模式下用于释放成员变量的方法,调用的时机是在dealloc方法调用的时候再调用。只有当前的类拥有实例变量的时候,类的信息中才会出现这个方法,属性也会在编译后变成实例变量,所以类中有属性也会有这个方法。

  2. 父类的实例变量不会使子类自带这个方法。

三、类的方法的存储(在ro中)

上一节中说过class_rw_tclass_ro_t都会存储类的方法,上面已经看过实例方法在rw中的存储了,下面看一下ro中是否也有实例方法的存储。

步骤类似,说的太多了,就直接上图了

图片.png

方法并不在baseMethodList里面。那么就需要找class_ro_t的源码了

图片.png

图中的方法是baseMethods。所以需要class_ro_t类型的对象$5调用baseMethods函数。

图片.png

直接取指针指向的内容

图片.png

这就和上面一样了,看源码,找method_list_t,发现是继承于entsize_list_tt的,而entsize_list_tt源码中有一个get()函数。所以method_list_t类型的$9可以调用它的父类的get()函数。

图片.png

又是method_t,上面过了,这里就不说了。直接调用big()函数。

图片.png

结论 : 类的实例方法也是存在于class_ro_t中的。也就是既存在于class_rw_t中也存在于class_ro_t中。

四、类方法的存储

这个就不多说了,JDPerson是继承于NSObject的,但是NSObject中是没有我们的类方法的,因为类方法是我们自己在子类JDPerson中定义的,那么类方法在哪里?

思路 : runtime中有一个常用的API,也是我们项目中用到的API : object_getClass获取对象的类信息。

1. object_getClass源码

图片.png

但是object_getClass的参数只能是实例对象,虽然说万物皆对象,但是直接将JDPerson填入肯定是会报错的。所以只能查看object_getClass的源码,看看可不可以利用它的实现原理,来找到JDPerson继承的类的信息。

object_getClass的源码如下 :

图片.png

getIsa() :

图片.png

很明显我们不是isTaggedPointer,所以会直接走return ISA(),因为isTaggedPointer带个非!

进入ISA() :

图片.png

进入getDecodedClass :

图片.png

mac环境下,SUPPORT_INDEXED_ISA为0,所以直接进getClass :

图片.png

如图中备注所示。

返回的是clsbits,而clsbits是isa的结构体成员bits来进行的赋值。关于isa可以看第四章

所以,其实父类的信息就是从isa中拿到的,并且是对isa做了一个&的操作,是&上一个ISA_MASK

看下ISA_MASK

图片.png

mac环境是x86,上面还有arm64的,就不看了。

2. 取JDPerson的isa指向的类

  1. 先打印JDPerson的内存,然后用isa和ISA_MASK&,得到的就是JDPerson的isa指向的类。

图片.png

  1. 打印这个JDPerson的isa指向的类的内存地址,然后平移32位,强转成class_data_bits_t

图片.png

  1. 后续的步骤和上面一致,直接上图。

图片.png

图片.png

类方法找到,在JDPerson的isa指向的类的bits中,而JDPerson的isa指向的类,我们通常称其为元类。

结论 : 类方法在类的元类的bits中存储。