类的布局——方法列表(2)

在上一篇的demo中,增加几个类方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//  ObjectA.h
@interface ObjectA : NSObject

@property(nonatomic,assign,readonly)BOOL b;

- (void)funcA;
- (void)funcB:(NSString *)str;

+ (void)funcC;
+ (void)funcD:(NSString *)str;
@end

// ObjectA.m
#import "ObjectA.h"
@implementation ObjectA
- (void)funcA {
NSLog(@"方法A");
}
- (void)funcB:(NSString *)str {
NSLog(@"方法B:%@", str);
}

+ (void)funcC {
NSLog(@"类方法C");
}
+ (void)funcD:(NSString *)str {
NSLog(@"类方法D: %@", str);
}
@end

用代码输出方法信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
// 定义 ISA_MASK 用于获取isa指针
#if __arm64__
#if TARGET_OS_EXCLAVEKIT
#define ISA_MASK 0xfffffffffffffff8ULL
#elif __has_feature(ptrauth_calls) || TARGET_OS_SIMULATOR
#define ISA_MASK 0x007ffffffffffff8ULL
#else
#define ISA_MASK 0x0000000ffffffff8ULL
#endif
#endif

// 定义 FAST_DATA_MASK 用于获取class_rw_t
#if TARGET_OS_EXCLAVEKIT
#define FAST_DATA_MASK 0x0000001ffffffff8UL
#elif TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
#define FAST_DATA_MASK 0x0f00007ffffffff8UL
#else
#define FAST_DATA_MASK 0x0f007ffffffffff8UL
#endif

// 定义方法结构体
struct method_t {
SEL sel;
char *types;
IMP imp;
};

// 定义方法列表结构体
struct method_list_t {
uint32_t entsizeAndFlags;
uint32_t count;
struct method_t elements[0];
};

// 从实例对象中获取isa指针
BytePtr _isaForObject(NSObject *obj) {
if (obj == nil) return NULL;
struct _object {
BytePtr isa;
};
struct _object *obj_ptr = (struct _object *)(__bridge void *)obj;
return (BytePtr)((int64_t)obj_ptr->isa & ISA_MASK);
}

// 从isa指针中获取方法列表和方法数
void _methodsWithIsa(BytePtr isa, struct method_t **methods, uint32_t *count) {
// 获取isa中的bits
uintptr_t bits = *((uintptr_t *)(isa + 8/*isa*/ + 8/*superclass*/ + 16/*cache*/));

// 获取 class_rw_t
uintptr_t class_rw_t = bits & FAST_DATA_MASK;

// 获取 ro_or_rw_ext
uintptr_t ro_or_rw_ext = *((uintptr_t *)(class_rw_t + 0x8));

// 获取class_ro_t
// 判断是class_rw_ext_t 还是 class_ro_t
uintptr_t class_ro_t = ro_or_rw_ext;
if (ro_or_rw_ext & 0x1) {
// class_rw_ext_t
class_ro_t = *((uintptr_t *)ro_or_rw_ext);
}

// 获取class_ro_t 中的 baseMethods
uintptr_t baseMethods = *(uintptr_t *)(class_ro_t + 4/*flags*/ + 4/*instanceStart*/ + 4/*instanceSize*/ + 4/*reserved*/ + 8/*ivarLayout*/ + 8/*name*/);

struct method_list_t *_method_list_t = (struct method_list_t *)baseMethods;
*count = _method_list_t->count;
*methods = _method_list_t->elements;
};

// 输出方法信息
void _printMethods(struct method_t *methods, uint32_t count) {
struct method_t *node = methods;
for (int i=0; i<count; i++) {
printf("=============================\n");
printf("SEL:%s\n", sel_getName(node->sel));
printf("types:%s\n",node->types);
printf("imp:0x%lx\n",(uintptr_t)node->imp);
node++;
}
printf("=============================\n");
}

// 调用
ObjectA *aObj = [[ObjectA alloc] init];

BytePtr a_isa = _isaForObject(aObj);
struct method_t *method = NULL;
uint32_t count;
_methodsWithIsa(a_isa, &method, &count);
_printMethods(method, count);

// 输出如下:
=============================
SEL:funcA
types:v16@0:8
imp:0x1009b5c58
=============================
SEL:funcB:
types:v24@0:8@16
imp:0x1009b5c84
=============================
SEL:b
types:B16@0:8
imp:0x1009b5d78
=============================

不难发现,刚刚新增的两个类方法并没有被输出。

原因是因为类方法和实例方法并不是存放在一起;在OC的设计中,有类的类的说法,即我们日常开发中的元类。而我们定义的类方法,就存储在元类中。

回顾一下类的定义:

1
2
3
4
5
6
7
8
9
struct objc_class : objc_object {
// Class ISA; // 继承自 struct objc_object
Class superclass;
cache_t cache; // formerly cache pointer and vtable
class_data_bits_t bits;
class_rw_t *data() const {
return bits.data();
}
};

之前我们把重心放在了bits上,现在我们回过头分析一下它的整个结构,就会发现,它也有一个isa指针(继承自objc_object)。元类就存储在类对象的isa中。元类也是objc_class类型。

元类也是objc_class结构体,那我们一样可以用上面的代码去解析它的方法列表(元类中只存储类方法):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
ObjectA *aObj = [[ObjectA alloc] init];

BytePtr a_isa = _isaForObject((NSObject *)aObj); // 获取aObj的isa指针 (类对象)
BytePtr meta_isa = _isaForObject((__bridge NSObject *)(void *)a_isa); // 获取类对象的isa指针
struct method_t *method = NULL;
uint32_t count;
_methodsWithIsa(meta_isa, &method, &count);
_printMethods(method, count);

// 输出
=============================
SEL:funcC
types:v16@0:8
imp:0x100cf5ce8
=============================
SEL:funcD:
types:v24@0:8@16
imp:0x100cf5d14
=============================