類(lèi)Class ,也可以稱(chēng)為類(lèi)對(duì)象蕉陋,在編譯時(shí)會(huì)轉(zhuǎn)成objc_class
, objc_class繼承自objc_object
,objc_object是結(jié)構(gòu)體:
struct objc_class {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
objc_class也是一個(gè)結(jié)構(gòu)體,以下是類(lèi)的結(jié)構(gòu):
struct objc_class : objc_object {
// Class ISA; // 8字節(jié)(指針是 8字節(jié)) ISA是默認(rèn)父類(lèi)中有
Class superclass; // 8字節(jié)
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);
}
以下是緩存類(lèi) cache_t 的結(jié)構(gòu):
struct cache_t {
struct bucket_t *_buckets; // 8字節(jié)
mask_t _mask; // 4字節(jié)
mask_t _occupied; // 4字節(jié)
如果我們?cè)陬?lèi)中添加了屬性 和 方法的話仰税,那他們具體在哪個(gè)地方存儲(chǔ)著呢?
顯然,我們?cè)陬?lèi)的結(jié)構(gòu)體中可以看到屬性 和 方法是存放在 class_data_bits_t
類(lèi)型的 bites中。具體如何確認(rèn)呢?
可以確認(rèn)在 objc_class結(jié)構(gòu)體中赋除,isa是指針占用 8字節(jié)阱缓,superclass同樣也是8字節(jié),緩存cache是16字節(jié)举农,這樣荆针,我們可以通過(guò)內(nèi)存偏移找到bits的內(nèi)存地址
如下,創(chuàng)建一個(gè)類(lèi)颁糟,添加成員變量hobby以及屬性nickName航背,添加實(shí)例方法和類(lèi)方法:
@interface LGPerson : NSObject{
NSString *hobby; //成員變量
}
@property (nonatomic, copy) NSString *nickName; //屬性
- (void)sayHello;
+ (void)sayHappy;
@end
然后創(chuàng)建 初始化
LGPerson *person = [LGPerson alloc];
Class pClass = object_getClass(person);
NSLog(@"%@ - %p",person,pClass);
查找流程如下:
我們先打個(gè)斷點(diǎn),然后通過(guò)lldb命令來(lái)查找對(duì)應(yīng)的屬性和方法 棱貌。
(lldb) p/x pClass //查找指針地址玖媚,目前找到的是isa的指針地址
(Class) $21 = 0x00000001000023b0 LGPerson
通過(guò)
內(nèi)存偏移
32 ( isa 8 ,superClass 8婚脱,cache是結(jié)構(gòu)體今魔,但是里面占用了16個(gè)字節(jié) ),0x00000001000023b0 + 32 障贸,對(duì)應(yīng)的16進(jìn)制為 0x00000001000023d0
(lldb) p (class_data_bits_t *)0x00000001000023d0
(class_data_bits_t *) $23 = 0x00000001000023d0
(lldb) p $23->data() //這里是調(diào)用 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")
}
}
//對(duì)于成員變量 在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
}
}
}
//同時(shí),屬性也會(huì)對(duì)應(yīng)生成一個(gè)成員變量在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)
}
}
}
以上片段中可以看到方法列表中 有一個(gè)
count = 4
,那么是有具體哪幾個(gè)方法呢袁波?
(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++的系統(tǒng)默認(rèn)方法
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" //實(shí)例方法
types = 0x0000000100001f8b "v16@0:8"
imp = 0x0000000100001b90 (LGTest`-[LGPerson sayHello] at LGPerson.m:13)
}
我們創(chuàng)建的類(lèi)中是應(yīng)該還有一個(gè) happy類(lèi)方法的瓦阐,但是沒(méi)有在baseMethodList中找到,那會(huì)在哪兒呢篷牌?同樣的操作垄分,再進(jìn)行一遍,只是中間多了一個(gè)元類(lèi)
(lldb) x/4gx pClass
0x1000023b0: 0x001d800100002389 0x0000000100afe140
0x1000023c0: 0x00000001003a1280 0x0000000000000000
(lldb) p/x 0x001d800100002389 & 0x00007ffffffffff8 //找到元類(lèi)
(long) $40 = 0x0000000100002388
(lldb) x/4gx 0x0000000100002388
0x100002388: 0x001d800100afe0f1 0x0000000100afe0f0
0x100002398: 0x0000000101e24ae0 0x0000000100000007
(lldb) p (class_data_bits_t *)0x1000023a8 //內(nèi)存偏移到 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)
}
}
}
以上娃磺,可以找到類(lèi)中的類(lèi)方法薄湿。由此可知,我們可以總結(jié)出:
1、類(lèi)(Class)最終是會(huì)編譯成 objc_class(繼承自objc_object).
2豺瘤、objc_class結(jié)構(gòu)體中含有(默認(rèn)的)ISA吆倦、(父類(lèi))superclass、(緩存)cache坐求、(存儲(chǔ)屬性和方法)bits
3蚕泽、在(class_data_bits_t)bits中,類(lèi)中的屬性會(huì)在 ro 的 baseProperties中,而對(duì)應(yīng)生成的成員變量會(huì)在 ro 的 ivars中桥嗤。
4须妻、在(class_data_bits_t)bits中,類(lèi)中的方法會(huì)在 ro 的 baseMethodList中,而類(lèi)中的類(lèi)方法
會(huì)在元類(lèi)
中的 ro 的 baseMethodList中泛领。
中間來(lái)個(gè)插曲: 為什么在外面isa是Class呢荒吏,因?yàn)閯?chuàng)建初始化isa時(shí)就強(qiáng)轉(zhuǎn)為Class類(lèi)型