iOS九阴真经:八、class_rw_t 探索

910 阅读2分钟

一、获取class_rw_t

在拿到 class_data_bits_t 的内存地址后,需要拿到里面的data,怎么拿?,我们看到源码是这么拿的:

image.png

那我们也假里假气的这么拿,通过lldb打印的方式去获取:

image.png

我们通过 p $2->data() 的方式拿到了 class_rw_t 的内存地址,并且通过内存地址取值的操作,p *($3)就拿到了class_rw_t的结构。($2$3都是 lldb 打印生成的变量名。)。

现在需要拿到,这个类里缓存的方法,属性,协议等等。先来看class_rw_t里有什么。

image.png

这么一看,貌似没有我们想要的东西,那就找方法,往下找:

image.png

这个不就是我们需要的么,为此,我们先来给 SHPerson 添加属性和方法:

@interface SHPerson : NSObject
 @property (nonatomic, copy) NSString *name;
 @property (nonatomic) NSInteger age;
 @end
 @implementation SHPerson
 - (void)setNickname:(NSString *)name {
 }
 
+ (instancetype)person {
    return [[SHPerson alloc] init];
}

二、获取 property_list_t

重新运行,重新拿到 class_rw_t

image.png

通过 lldb 打印 p $3.properties(),拿到了类型为 property_array_t$4,通过property_array_t在运行时的结构看到里面有 ptr 这个指针地址。打印出来发现它是一个 property_list_t 类型的,来看看源码中 property_list_t 是什么:

image.png

源码中什么都没有,但它继承自 entsize_list_tt

image.png

里面有一个 get 获取元素的方法,get 里调用 getOrEnd,发现,getOrEnd的实现,是通过内存平移的方式来拿到对应的元素

这时,我们试试在lldb打印中通过get方法拿我们的属性相关的东西,但是因为 lldb 打印的原因,得重新运行,并且我们知道 ptr 的类型为 property_list_t,在打印到property_array_t的时候,可以直接强制将 ptr 转换为 property_list_t,或者直接 p *$6,不然在lldb中不能通过get方法打印出相关的东西。

image.png

通过lldb打印,确实有属性相关的东西。

三、获取 method_t

methods() 返回的是一个 method_array_t 类型的,拿到method_list_t,他一样继承自entsize_list_tt,但通过get方法打印的出来的是一个里面啥都没有的 method_t 类型:

image.png

不要着急,来看一下 method_t 的结构:

image.png

可以看到,有个结构体big,并且结构体big里有SEL nameconst char *typesMethodListIMP imp

再往下看:

image.png

来试着打印这三个方法看看效果:

image.png

接下来把所有的方法打印出来,看看是否对得上我们声明的方法,另外,这里就不做协议方法的打印了,其实也是可以打印出来的,感兴趣的也可以研究下。

打印method_t中的name方法.png

打印结果发现有一个.cxx_destruct方法, .cxx_destruct方法原本是为了C++对象析构的,ARC借用了这个方法插入代码实现了自动内存释放的工作。

而且,并没有发现我们声明的 + (instancetype)person,说明类方法并不存于类对象!

那么类方法存储在哪里呢,其实类方法存储在元类里面,类和元类的结构是一样的,只是存储的内容有区别,感兴趣的可以参照以上的步骤,去获取元类中存储的类方法。