OC类结构分析
类Class ,也可以称为类对象,在编译时会转成objc_class
, objc_class继承自objc_object
,objc_object是结构体:
struct objc_class {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
objc_class也是一个结构体,以下是类的结构:
struct objc_class : objc_object {
// Class ISA; // 8字节(指针是 8字节) ISA是默认父类中有
Class superclass; // 8字节
cache_t cache; // 16 不是8 // formerly cache pointer and vtable
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
class_rw_t *data() {
return bits.data();
}
void setData(class_rw_t *newData) {
bits.setData(newData);
}
以下是缓存类 cache_t 的结构:
struct cache_t {
struct bucket_t *_buckets; // 8字节
mask_t _mask; // 4字节
mask_t _occupied; // 4字节
如果我们在类中添加了属性 和 方法的话,那他们具体在哪个地方存储着呢?
显然,我们在类的结构体中可以看到属性 和 方法是存放在 class_data_bits_t
类型的 bites中。具体如何确认呢?
可以确认在 objc_class结构体中,isa是指针占用 8字节,superclass同样也是8字节,缓存cache是16字节,这样,我们可以通过内存偏移找到bits的内存地址
如下,创建一个类,添加成员变量hobby以及属性nickName,添加实例方法和类方法:
@interface LGPerson : NSObject{
NSString *hobby; //成员变量
}
@property (nonatomic, copy) NSString *nickName; //属性
- (void)sayHello;
+ (void)sayHappy;
@end
然后创建 初始化
LGPerson *person = [LGPerson alloc];
Class pClass = object_getClass(person);
NSLog(@"%@ - %p",person,pClass);
查找流程如下:
我们先打个断点,然后通过lldb命令来查找对应的属性和方法 。
(lldb) p/x pClass //查找指针地址,目前找到的是isa的指针地址
(Class) $21 = 0x00000001000023b0 LGPerson
通过
内存偏移
32 ( isa 8 ,superClass 8,cache是结构体,但是里面占用了16个字节 ),0x00000001000023b0 + 32 ,对应的16进制为 0x00000001000023d0
(lldb) p (class_data_bits_t *)0x00000001000023d0
(class_data_bits_t *) $23 = 0x00000001000023d0
(lldb) p $23->data() //这里是调用 class_rw_t *data() { return bits.data(); }方法得到bits里面的东西
(class_rw_t *) $24 = 0x000000010194f560
(lldb) p $24->ro
(const class_ro_t *) $26 = 0x0000000100002308
(lldb) p *$26 //打印出里面的值
(const class_ro_t) $27 = {
flags = 388
instanceStart = 8
instanceSize = 24
reserved = 0
ivarLayout = 0x0000000100001f89 "\x02"
name = 0x0000000100001f80 "LGPerson"
baseMethodList = 0x0000000100002240
baseProtocols = 0x0000000000000000
ivars = 0x00000001000022a8
weakIvarLayout = 0x0000000000000000
baseProperties = 0x00000001000022f0
} //这里面可以看到baseProperties ,属性便可以在里面找到
(lldb) p *$27.baseProperties
(property_list_t) $29 = {
entsize_list_tt<property_t, property_list_t, 0> = {
entsizeAndFlags = 16
count = 1
first = (name = "nickName", attributes = "T@\"NSString\",C,N,V_nickName")
}
}
//对于成员变量 在ivars中可以找到
(lldb) p $27.ivars
(const ivar_list_t *const) $30 = 0x00000001000022a8
(lldb) p *$30
(const ivar_list_t) $31 = {
entsize_list_tt<ivar_t, ivar_list_t, 0> = {
entsizeAndFlags = 32
count = 2
first = {
offset = 0x0000000100002378
name = 0x0000000100001e64 "hobby"
type = 0x0000000100001fa6 "@\"NSString\""
alignment_raw = 3
size = 8
}
}
}
//同时,属性也会对应生成一个成员变量在ivars中
(lldb) p $30.get(1)
(ivar_t) $32 = {
offset = 0x0000000100002380
name = 0x0000000100001e6a "_nickName"
type = 0x0000000100001fa6 "@\"NSString\""
alignment_raw = 3
size = 8
}
Fix-it applied, fixed expression was:
$30->get(1)
可以肯定的是属性在 class_data_bits_t bits 里面,而方法呢?同样在ivars中可以找到方法,如下
(lldb) p $27.baseMethodList //同样在ivars中可以找到方法
(method_list_t *const) $33 = 0x0000000100002240
(lldb) p *$33
(method_list_t) $34 = {
entsize_list_tt<method_t, method_list_t, 3> = {
entsizeAndFlags = 26
count = 4
first = {
name = "sayHello"
types = 0x0000000100001f8b "v16@0:8"
imp = 0x0000000100001b90 (LGTest`-[LGPerson sayHello] at LGPerson.m:13)
}
}
}
以上片段中可以看到方法列表中 有一个
count = 4
,那么是有具体哪几个方法呢?
(lldb) p $34.get(1)
(method_t) $35 = {
name = "nickName" ///get方法
types = 0x0000000100001f93 "@16@0:8"
imp = 0x0000000100001bf0 (LGTest`-[LGPerson nickName] at LGPerson.h:17)
}
(lldb) p $34.get(2)
(method_t) $36 = {
name = "setNickName:" ///set方法
types = 0x0000000100001f9b "v24@0:8@16"
imp = 0x0000000100001c20 (LGTest`-[LGPerson setNickName:] at LGPerson.h:17)
}
(lldb) p $34.get(3)
(method_t) $37 = {
name = ".cxx_destruct" //C++的系统默认方法
types = 0x0000000100001f8b "v16@0:8"
imp = 0x0000000100001c60 (LGTest`-[LGPerson .cxx_destruct] at LGPerson.m:11)
}
(lldb) p $34.get(0)
(method_t) $38 = {
name = "sayHello" //实例方法
types = 0x0000000100001f8b "v16@0:8"
imp = 0x0000000100001b90 (LGTest`-[LGPerson sayHello] at LGPerson.m:13)
}
我们创建的类中是应该还有一个 happy类方法的,但是没有在baseMethodList中找到,那会在哪儿呢?同样的操作,再进行一遍,只是中间多了一个元类
(lldb) x/4gx pClass
0x1000023b0: 0x001d800100002389 0x0000000100afe140
0x1000023c0: 0x00000001003a1280 0x0000000000000000
(lldb) p/x 0x001d800100002389 & 0x00007ffffffffff8 //找到元类
(long) $40 = 0x0000000100002388
(lldb) x/4gx 0x0000000100002388
0x100002388: 0x001d800100afe0f1 0x0000000100afe0f0
0x100002398: 0x0000000101e24ae0 0x0000000100000007
(lldb) p (class_data_bits_t *)0x1000023a8 //内存偏移到 bits 部分
(class_data_bits_t *) $41 = 0x00000001000023a8
(lldb) p $41->data()
(class_rw_t *) $42 = 0x000000010194f520
(lldb) p $42->ro
(const class_ro_t *) $43 = 0x00000001000021f8
(lldb) p *$43
(const class_ro_t) $44 = {
flags = 389
instanceStart = 40
instanceSize = 40
reserved = 0
ivarLayout = 0x0000000000000000
name = 0x0000000100001f80 "LGPerson"
baseMethodList = 0x00000001000021d8
baseProtocols = 0x0000000000000000
ivars = 0x0000000000000000
weakIvarLayout = 0x0000000000000000
baseProperties = 0x0000000000000000
}
(lldb) p $44.baseMethodList
(method_list_t *const) $45 = 0x00000001000021d8
(lldb) p *$45
(method_list_t) $46 = {
entsize_list_tt<method_t, method_list_t, 3> = {
entsizeAndFlags = 26
count = 1
first = {
name = "sayHappy"
types = 0x0000000100001f8b "v16@0:8"
imp = 0x0000000100001bc0 (LGTest`+[LGPerson sayHappy] at LGPerson.m:17)
}
}
}
以上,可以找到类中的类方法。由此可知,我们可以总结出:
1、类(Class)最终是会编译成 objc_class(继承自objc_object).
2、objc_class结构体中含有(默认的)ISA、(父类)superclass、(缓存)cache、(存储属性和方法)bits
3、在(class_data_bits_t)bits中,类中的属性会在 ro 的 baseProperties中,而对应生成的成员变量会在 ro 的 ivars中。
4、在(class_data_bits_t)bits中,类中的方法会在 ro 的 baseMethodList中,而类中的类方法
会在元类
中的 ro 的 baseMethodList中。
中间来个插曲: 为什么在外面isa是Class呢,因为创建初始化isa时就强转为Class类型