一、获取class_rw_t
在拿到 class_data_bits_t 的内存地址后,需要拿到里面的data,怎么拿?,我们看到源码是这么拿的:
那我们也假里假气的这么拿,通过lldb打印的方式去获取:
我们通过 p $2->data() 的方式拿到了 class_rw_t 的内存地址,并且通过内存地址取值的操作,p *($3)就拿到了class_rw_t的结构。($2和$3都是 lldb 打印生成的变量名。)。
现在需要拿到,这个类里缓存的方法,属性,协议等等。先来看class_rw_t里有什么。
这么一看,貌似没有我们想要的东西,那就找方法,往下找:
这个不就是我们需要的么,为此,我们先来给 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:
通过 lldb 打印 p $3.properties(),拿到了类型为 property_array_t 的 $4,通过property_array_t在运行时的结构看到里面有 ptr 这个指针地址。打印出来发现它是一个 property_list_t 类型的,来看看源码中 property_list_t 是什么:
源码中什么都没有,但它继承自 entsize_list_tt。
里面有一个 get 获取元素的方法,get 里调用 getOrEnd,发现,getOrEnd的实现,是通过内存平移的方式来拿到对应的元素
这时,我们试试在lldb打印中通过get方法拿我们的属性相关的东西,但是因为 lldb 打印的原因,得重新运行,并且我们知道 ptr 的类型为 property_list_t,在打印到property_array_t的时候,可以直接强制将 ptr 转换为 property_list_t,或者直接 p *$6,不然在lldb中不能通过get方法打印出相关的东西。
通过lldb打印,确实有属性相关的东西。
三、获取 method_t
methods() 返回的是一个 method_array_t 类型的,拿到method_list_t,他一样继承自entsize_list_tt,但通过get方法打印的出来的是一个里面啥都没有的 method_t 类型:
不要着急,来看一下 method_t 的结构:
可以看到,有个结构体big,并且结构体big里有SEL name,const char *types,MethodListIMP imp。
再往下看:
来试着打印这三个方法看看效果:
接下来把所有的方法打印出来,看看是否对得上我们声明的方法,另外,这里就不做协议方法的打印了,其实也是可以打印出来的,感兴趣的也可以研究下。
打印结果发现有一个.cxx_destruct方法, .cxx_destruct方法原本是为了C++对象析构的,ARC借用了这个方法插入代码实现了自动内存释放的工作。
而且,并没有发现我们声明的 + (instancetype)person,说明类方法并不存于类对象!
那么类方法存储在哪里呢,其实类方法存储在元类里面,类和元类的结构是一样的,只是存储的内容有区别,感兴趣的可以参照以上的步骤,去获取元类中存储的类方法。