1掉丽、首先拋出問題 ? 類的結(jié)構(gòu)是什么码邻?
首先創(chuàng)建一個LGPesron的類窥摄,去cpp文件中。
利用clang編譯成cpp源碼(先cd到當(dāng)前文件的目錄):
```
clang -rewrite-objc main.m -o main.cpp
存在UIKit等其他動態(tài)引用庫時:
clang -rewrite-objc -fobjc-arc -fobjc-runtime=ios-13.0.0 -isysroot/Application/Xcode.app/Comtents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator13.0.sdk main.m
xcrun xcode 命令
模擬器:xcrun -sdk iphonesimulator clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp
真機(jī):xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp
```
打開cpp文件中全局搜索LGPerson测蹲,可以看到
typedef struct objc_object LGPerson;
struct objc_class {
? ? Class_Nonnullisa__attribute__((deprecated));
}__attribute__((unavailable));
在搜索objc_class 莹捡,可以看到
typedef struct objc_class *Class;
以上可以看到Class的真正類型為objc_class。
通過上面的探索扣甲,我們可以看到類是一種結(jié)構(gòu)體篮赢,來自于我們的底層編譯 ?objc_class ?*Class,類就是Class. ?往下層走我們可以看到objc_class的結(jié)構(gòu)體,這個結(jié)構(gòu)體來自于一個繼承objc_object琉挖,這個結(jié)構(gòu)體里面我們可以看到類的幾個屬性:
// Class ISA; // 8
? ? Class superclass;// 8
? ? cache_t cache;? ? // 16 不是8? ? ? ? // formerly cache pointer and vtable
? ? class_data_bits_t bits;
2启泣、我們通常都是用類定義一些東西:屬性和方法,那這些屬性和方法的定義在哪找到示辈?怎么去找寥茫?
在上面我們已經(jīng)知道了類里面有Class ISA;?Class superclass;?cache_t cache;class_data_bits_t bits;這幾個屬性矾麻。
首先我們知道isa的指針是關(guān)聯(lián)對象和類坠敷,superClass指向繼承類,那么類的成員能夠存儲的地方就只有cache和bits
先看一下cache的結(jié)構(gòu)體定義(不是一個結(jié)構(gòu)體指針射富,是一個結(jié)構(gòu)體)膝迎,其中 mask_t為固定的4字節(jié)類型的值,而bucket_t則是一個8字節(jié)的指針胰耗,都不能存放我們定義的屬性值限次,所以可以排除cache,這里也看出 cache的內(nèi)存大小只有4+4+8=16字節(jié)
structcache_t {
? ? structbucket_t*_buckets;// 8
? ? mask_t_mask;? ? ? ? ? ? ? // 4
? ? mask_t_occupied;? ? ? ? ? // 4
所以可以猜測類的方法和屬性在bits里面柴灯,繼續(xù)看源碼:
structobjc_class :objc_object{
? ? // Class ISA;
? ? Classsuper class;
? ? cache_t cache;? ? ? ? ? ? // 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();
? ? }
我們可以看到class_rw_t 點擊去:
structclass_rw_t {
? // Be warned that Symbolication knows the layout of this structure.
? ? uint32_t flags;
? ? uint32_t version;
? ? constclass_ro_t *ro;
? ? method_array_t ?methods;
? ? property_array_t properties;
? ? protocol_array_t ?protocols;
? ? Class firstSubclass;
? ? Class nextSiblingClass;
? ? char *demangledName;
#if SUPPORT_INDEXED_ISA
? ? uint32_t index;
#endif
由上我們是不是感覺類的屬性和方法存在class_rw_t里面了卖漫,我們驗證一下。
首先創(chuàng)建一個類LGPerson,添加成員變量hobby和屬性nickName.添加示例方法和類方法赠群。
@interfaceLGPerson :NSObject{
? ? NSString*hobby;
}
@property (nonatomic, copy) NSString *nickName;
- (void)sayHello;
+ (void)sayHappy;
@end
main函數(shù)中
?LGPerson*person = [LGPersonalloc];
? NSLog(@"%@ - %p",person,pClass);
xcode控制臺:
x/4gx 打印person.class的內(nèi)容羊始,但是除了第一以及第二的內(nèi)存,是我們熟悉的isa以及superClass指針以外查描,第三塊地址的內(nèi)容我們完全不知曉突委,第四塊地址直接就不存在
(lldb) x/4gx person.class
0x100002338: 0x001d800100002311 0x0000000100afe140
0x100002348: 0x0000000101239a70 0x0000000200000003
按照Class結(jié)構(gòu)體的成員定義順序,以及內(nèi)存對齊原則冬三,我們嘗試用指針偏移的方法匀油,來找到第四塊地址bits的所在,并且看看bits存放的內(nèi)容是什么
(lldb) p/x 0x100002358
(long) $1 = 0x0000000100002358
這樣我們什么也看不出來勾笆,看源碼敌蚜,我們用class_data_bits_t強(qiáng)轉(zhuǎn)
(lldb) p (class_data_bits_t *)0x0000000100002358
(class_data_bits_t *) $2 = 0x0000000100002358
打印出class_data_bits_t的結(jié)構(gòu)體∥炎Γ看上面源碼我們之后rw是返回的是bits.data()弛车,我們也調(diào)用一下齐媒,看看輸出啥
(lldb) p $2->data()
(class_rw_t *) $3 = 0x000000010100cbe0
(lldb) p *$3
(class_rw_t) $4 = {
? flags = 2148139008
? version = 0
? ro = 0x0000000100002288
? methods = {
? ? list_array_tt = {
?? ? ? = {
? ? ? ? list = 0x00000001000021d8
? ? ? ? arrayAndFlag = 4294975960
? ? ? }
? ? }
? }
? properties = {
? ? list_array_tt = {
?? ? ? = {
? ? ? ? list = 0x0000000100002270
? ? ? ? arrayAndFlag = 4294976112
? ? ? }
? ? }
? }
? protocols = {
? ? list_array_tt = {
?? ? ? = {
? ? ? ? list = 0x0000000000000000
? ? ? ? arrayAndFlag = 0
? ? ? }
? ? }
? }
? firstSubclass = nil
? nextSiblingClass = NSDate
? demangledName = 0x0000000000000000
}
通過上面輸出我們可看到rw里面的properties里面并沒有存儲內(nèi)容,那我們的成員變量和屬性存在哪呢纷跛?在源碼中我們直接被??method_array_t ?methods; property_array_t properties;protocol_array_t ?protocols;吸引里初,是否忘記了一個constclass_ro_t *ro;點擊去:
structclass_ro_t {
? ? uint32_tflags;
? ? uint32_tinstanceStart;
? ? uint32_tinstanceSize;
#ifdef __LP64__
? ? uint32_treserved;
#endif
? ? constuint8_t* ivarLayout;
? ? constchar* name;
? ? method_list_t* baseMethodList;
? ? protocol_list_t* baseProtocols;
? ? constivar_list_t* ivars;
? ? constuint8_t* weakIvarLayout;
? ? property_list_t*baseProperties;
? ? method_list_t*baseMethods()const{
? ? ? ? return baseMethodList;
? ? }
};
我們發(fā)現(xiàn)這里面也有baseMethodList,baseProtocols忽舟,還有ivars,按照上面控制臺來一遍淮阐;
?x/4gx person.class
0x100002338: 0x001d800100002311 0x0000000100afe140
0x100002348: 0x0000000101239a70 0x0000000200000003
(lldb) p/x 0x100002358
(long) $12 = 0x0000000100002358
(lldb) p (class_data_bits_t *)0x0000000100002358
(class_data_bits_t *) $13 = 0x0000000100002358
(lldb) p $13->data()
(class_rw_t *) $14 = 0x000000010100cbe0
(lldb) p $14->ro
(const class_ro_t *) $16 = 0x0000000100002288
(lldb) p *$16
(const class_ro_t) $17 = {
? flags = 388
? instanceStart = 8
? instanceSize = 24
? reserved = 0
? ivarLayout = 0x0000000100001f8d "\x02"
? name = 0x0000000100001f84 "LGPerson"
? baseMethodList = 0x00000001000021d8
? baseProtocols = 0x0000000000000000
? ivars = 0x0000000100002228
? weakIvarLayout = 0x0000000000000000
? baseProperties = 0x0000000100002270
}
(lldb) p $17.baseProperties
(property_list_t *const) $18 = 0x0000000100002270
(lldb) p *$18
(property_list_t) $19 = {
? entsize_list_tt = {
? ? entsizeAndFlags = 16
? ? count = 1
? ? first = (name = "nickName", attributes = "T@"NSString",C,N,V_nickName")
? }
}
我們想要的nickName出來了叮阅,那hobby呢?hobby肯定沒在baseProperties里面泣特,這個里面count=1浩姥,只存了一個值。hobby是成員變量状您,根據(jù)ro里面的字面意思肯定在ivars里面勒叠,我們打印看一下:
(lldb) p $17.ivars
(const ivar_list_t *const) $20 = 0x0000000100002228
(lldb) p *$20
(const ivar_list_t) $21 = {
? entsize_list_tt = {
? ? entsizeAndFlags = 32
? ? count = 2
? ? first = {
? ? ? offset = 0x0000000100002308
? ? ? name = 0x0000000100001e98 "hobby"
? ? ? type = 0x0000000100001faa "@"NSString""
? ? ? alignment_raw = 3
? ? ? size = 8
? ? }
? }
}
hobby出來了,眼睛毒辣的朋友會看到這里的count=2了,那是不是還有一個東西存在這呢膏孟?成員變量和屬性是有區(qū)別的眯分,在類中,我們定義的屬性編譯器也會自動的生成一個成員變量_nickName,所以我們在打悠馍!:
(lldb) p $21.get(1)
(ivar_t) $22 = {
? offset = 0x0000000100002300
? name = 0x0000000100001e9e "_nickName"
? type = 0x0000000100001faa "@"NSString""
? alignment_raw = 3
? size = 8
}
屬性和成員變量都知道存在那了弊决,那方法呢?字面意思baseMethodList魁淳,是不是在這里面飘诗,我們驗證:
(lldb) p *$6
(const class_ro_t) $7 = {
? flags = 388
? instanceStart = 8
? instanceSize = 24
? reserved = 0
? ivarLayout = 0x0000000100001f8d "\x02"
? name = 0x0000000100001f84 "LGPerson"
? baseMethodList = 0x0000000100002238
? baseProtocols = 0x0000000000000000
? ivars = 0x00000001000022a0
? weakIvarLayout = 0x0000000000000000
? baseProperties = 0x00000001000022e8
}
(lldb) p $7.baseMethodList
(method_list_t *const) $8 = 0x0000000100002238
(lldb) p *$8
(method_list_t) $9 = {
? entsize_list_tt = {
? ? entsizeAndFlags = 26
? ? count = 4
? ? first = {
? ? ? name = "sayHello"
? ? ? types = 0x0000000100001f8f "v16@0:8"
? ? ? imp = 0x0000000100001b90 (LGTest`-[LGPerson sayHello] at LGPerson.m:13)
? ? }
? }
}
sayHello出來了,但是count=4這是為什么界逛?不是只有2個方法嗎昆稿?我們打印:
(lldb) p $9.get(0)
(method_t) $10 = {
? name = "sayHello"
? types = 0x0000000100001f8f "v16@0:8"
? imp = 0x0000000100001b90 (LGTest`-[LGPerson sayHello] at LGPerson.m:13)
}
(lldb) p $9.get(1)
(method_t) $11 = {
? name = "nickName"
? types = 0x0000000100001f97 "@16@0:8"
? imp = 0x0000000100001bf0 (LGTest`-[LGPerson nickName] at LGPerson.h:17)
}
(lldb) p $9.get(2)
(method_t) $12 = {
? name = "setNickName:"
? types = 0x0000000100001f9f "v24@0:8@16"
? imp = 0x0000000100001c20 (LGTest`-[LGPerson setNickName:] at LGPerson.h:17)
}
(lldb) p $9.get(3)
(method_t) $13 = {
? name = ".cxx_destruct"
? types = 0x0000000100001f8f "v16@0:8"
? imp = 0x0000000100001c60 (LGTest`-[LGPerson .cxx_destruct] at LGPerson.m:11)
}
從以上結(jié)果看出息拜,里面存了nickName的setter和getter方法溉潭,還有c++的析構(gòu)函數(shù)destruct方法,怎么沒有類方法+(void)sayHappy少欺?我們都知道類方法是存在元類中的岛抄,我們?nèi)ピ愔姓遥?/p>
(lldb) x/4gx person
0x101840260: 0x001d8001000023b5 0x0000000000000000
0x101840270: 0x0000000000000000 0x0000000000000000
(lldb) p/x 0x001d8001000023b5 & 0x00007ffffffffff8
(long) $1 = 0x00000001000023b0
(lldb) x/4gx 0x00000001000023b0
0x1000023b0: 0x001d800100002389 0x0000000100afe140
0x1000023c0: 0x00000001003a0e80 0x0000000000000000
(lldb) p (class_data_bits_t *)0x1000023d0
(class_data_bits_t *) $2 = 0x00000001000023d0
(lldb) p $2->data()
(class_rw_t *) $3 = 0x00000001018408c0
(lldb) p $3.ro
(const class_ro_t *) $4 = 0x0000000100002300
? Fix-it applied, fixed expression was:?
? ? $3->ro
(lldb) p $3->ro
(const class_ro_t *) $5 = 0x0000000100002300
(lldb) p *$5
(const class_ro_t) $6 = {
? flags = 388
? instanceStart = 8
? instanceSize = 24
? reserved = 0
? ivarLayout = 0x0000000100001f8d "\x02"
? name = 0x0000000100001f84 "LGPerson"
? baseMethodList = 0x0000000100002238
? baseProtocols = 0x0000000000000000
? ivars = 0x00000001000022a0
? weakIvarLayout = 0x0000000000000000
? baseProperties = 0x00000001000022e8
}
(lldb) p $6.baseMethodList
(method_list_t *const) $7 = 0x0000000100002238
(lldb) p *$7
(method_list_t) $8 = {
? entsize_list_tt = {
? ? entsizeAndFlags = 26
? ? count = 4
? ? first = {
? ? ? name = "sayHello"
? ? ? types = 0x0000000100001f8f "v16@0:8"
? ? ? imp = 0x0000000100001b90 (LGTest`-[LGPerson sayHello] at LGPerson.m:13)
? ? }
? }
}
總結(jié):
屬性和成員變量存在ro的ivars里面,并且屬性還存在ro的baseProperties中狈茉。
實例方法和類方法:我們自定義的實例方法和系統(tǒng)自動生成的setter夫椭、getter和c++的方法存在ro的baseMethodList,也就是類中氯庆。類方法存在元類中蹭秋。