根据新的开源代码,类的结构发生了改变,从这里开始我将采用新的objc-781
源码
链接: https://pan.baidu.com/s/1hKjeORBKUK58f-4v0DMU6A
密码: 8ejw
前面已经分析了类的内存结构,本文主要用来论证对象方法存放在类中,类方法存放在元类中。
@interface LRPerson : NSObject
@property (nonatomic,copy) NSString *name;
- (void)sayHello;
+ (void)sayHappy;
@end
@implementation LRPerson
- (void)sayHello {}
+ (void)sayHappy {}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
LRPerson *p = [LRPerson alloc];
p.name = @"LR";
NSLog(@"Hello, World!");
}
return 0;
}
- 在
NSLog
处打上断点,开始LLDB调试 x/4gx LRPerson.class
(lldb) x/4gx LRPerson.class
0x100002260: 0x0000000100002238 0x00000001003f0140
0x100002270: 0x000000010125c7d0 0x0001801c00000003
(lldb)
p (class_data_bits_t *)0x100002280
(lldb) p (class_data_bits_t *)0x100002280
(class_data_bits_t *) $1 = 0x0000000100002280
(lldb)
p $1->data()
(lldb) p $1->data()
(class_rw_t *) $2 = 0x000000010125c770
(lldb)
p $2->methods()
(lldb) p $2->methods()
(const method_array_t) $3 = {
list_array_tt<method_t, method_list_t> = {
= {
list = 0x00000001000020e8
arrayAndFlag = 4294975720
}
}
}
(lldb)
p $3.list
(lldb) p $3.list
(method_list_t *const) $4 = 0x00000001000020e8
(lldb)
p *$4
(lldb) p *$4
(method_list_t) $5 = {
entsize_list_tt<method_t, method_list_t, 3> = {
entsizeAndFlags = 26
count = 4
first = {
name = "sayHello"
types = 0x0000000100000f5a "v16@0:8"
imp = 0x0000000100000d80 (KCObjc`-[LRPerson sayHello] at main.m:22)
}
}
}
(lldb)
我们发现count = 4
,总共有4个方法,分别打印出来
p $5.get(0)
(lldb) p $5.get(0)
(method_t) $7 = {
name = "sayHello"
types = 0x0000000100000f5a "v16@0:8"
imp = 0x0000000100000d80 (KCObjc`-[LRPerson sayHello] at main.m:22)
}
(lldb)
p $5.get(1)
(lldb) p $5.get(1)
(method_t) $8 = {
name = ".cxx_destruct"
types = 0x0000000100000f5a "v16@0:8"
imp = 0x0000000100000d90 (KCObjc`-[LRPerson .cxx_destruct] at main.m:20)
}
p $5.get(2)
(lldb) p $5.get(2)
(method_t) $9 = {
name = "name"
types = 0x0000000100000f6e "@16@0:8"
imp = 0x0000000100000dc0 (KCObjc`-[LRPerson name] at main.m:12)
}
p $5.get(3)
(lldb) p $5.get(3)
(method_t) $10 = {
name = "setName:"
types = 0x0000000100000f76 "v24@0:8@16"
imp = 0x0000000100000df0 (KCObjc`-[LRPerson setName:] at main.m:12)
}
p $5.get(4)
(lldb) p $5.get(4)
Assertion failed: (i < count), function get, file /Users/liuyang/Desktop/可编译objc源码/runtime/objc-runtime-new.h, line 438.
error: Execution was interrupted, reason: signal SIGABRT.
此时数组越界。
我们可以看到对象方法 sayHello
,属性的setter
和getter
方法,还有cxx_destruct
析构方法,都存放在class_rw_t
的method_list_t
中,唯独不见类方法+ (void)sayHappy;
的踪影。
借助machoView分析类方法
1.machoView下载
链接: https://pan.baidu.com/s/1guWQKZV0SoX3umoBSHaukw
密码: gqio
2.编译生成可执行文件
- 我们的代码编译会生成一个黑色可执行文件,
Show in Finder -> 删除 -> command+b
编译会重新生成
3.借助machoView
分析
编译之后,其实我们的整个数据都已经加载到内存中了,将黑色可执行文件拖入machoView
中,我们可以在Functions
区域找到所有的对象方法和类方法,类方法的的确确已经加载到内存中了,那么method_list_t
为什么找不到类方法?类方法存储在哪里?
其实OC
编译到底层,并不会区分对象方法和类方法,我们在源码中也没有找到instance_ method_list_t
或者class_ method_list_t
,在底层只有方法。对象方法存在类里面,以此为依据我们可以大胆假设,类方法是否存在元类里面?
下面我们用LLDB来探索
LLDB探索类方法是否存储在元类中
-
x/4gx p.class
拿到类
(lldb) x/4gx p.class
0x100002260: 0x0000000100002238 0x00000001003f0140
0x100002270: 0x00000001011040a0 0x0001801c00000007
(lldb)
-
p/x 0x0000000100002238 & 0x00007ffffffffff8ULL
拿到元类指针地址
(lldb) p/x 0x0000000100002238 & 0x00007ffffffffff8ULL
(unsigned long long) $1 = 0x0000000100002238
-
po 0x0000000100002238
验证确实是元类
(lldb) po 0x0000000100002238
LRPerson
(lldb)
x/4gx 0x0000000100002238
(lldb) x/4gx 0x0000000100002238
0x100002238: 0x00000001003f00f0 0x00000001003f00f0
0x100002248: 0x000000010155f840 0x0001e03500000003
(lldb)
-
p (class_data_bits_t *)0x100002238
指针偏移、强转得到class_data_bits_t
(lldb) p (class_data_bits_t *)0x100002238
(class_data_bits_t *) $4 = 0x0000000100002238
(lldb)
-
p $4->data()
拿到class_rw_t
(lldb) p $4->data()
(class_rw_t *) $5 = 0x00000001003f00f0
(lldb)
-
p $5->methods()
拿到method_array_t
(lldb) p $5->methods()
(const method_array_t) $6 = {
list_array_tt<method_t, method_list_t> = {
= {
list = 0x00000001012040e4
arrayAndFlag = 4313858276
}
}
}
(lldb)
-
p $6.list
拿到method_list_t
(lldb) p $6.list
(method_list_t *const) $7 = 0x00000001012040e4
(lldb)
-
p *$7
读取$7
(lldb) p *$7
(method_list_t) $8 = {
entsize_list_tt<method_t, method_list_t, 3> = {
entsizeAndFlags = 26
count = 1
first = {
name = "sayHappy"
types = 0x0000000100000f5a "v16@0:8"
imp = 0x0000000100000d70 (KCObjc`+[LRPerson sayHappy] at main.m:24)
}
}
}
(lldb)
类方法sayHappy
正在其中,如此证明类方法确实存在于元类里
。