根據(jù)新的開源代碼钟病,類的結(jié)構(gòu)發(fā)生了改變蛇更,從這里開始我將采用新的objc-781
源碼
鏈接: https://pan.baidu.com/s/1hKjeORBKUK58f-4v0DMU6A
密碼: 8ejw
前面已經(jīng)分析了類的內(nèi)存結(jié)構(gòu)戒良,本文主要用來論證對象方法存放在類中们拙,類方法存放在元類中编兄。
@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調(diào)試 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)
我們發(fā)現(xiàn)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.
此時數(shù)組越界。
我們可以看到對象方法 sayHello
蛉签,屬性的setter
和getter
方法胡陪,還有cxx_destruct
析構(gòu)方法,都存放在class_rw_t
的method_list_t
中碍舍,唯獨不見類方法+ (void)sayHappy;
的蹤影柠座。
借助machoView分析類方法
1.machoView下載
鏈接: https://pan.baidu.com/s/1guWQKZV0SoX3umoBSHaukw
密碼: gqio
2.編譯生成可執(zhí)行文件
- 我們的代碼編譯會生成一個黑色可執(zhí)行文件,
Show in Finder -> 刪除 -> command+b
編譯會重新生成
image.png
3.借助machoView
分析
編譯之后片橡,其實我們的整個數(shù)據(jù)都已經(jīng)加載到內(nèi)存中了妈经,將黑色可執(zhí)行文件拖入machoView
中,我們可以在Functions
區(qū)域找到所有的對象方法和類方法捧书,類方法的的確確已經(jīng)加載到內(nèi)存中了吹泡,那么method_list_t
為什么找不到類方法?類方法存儲在哪里鳄厌?
其實OC
編譯到底層荞胡,并不會區(qū)分對象方法和類方法,我們在源碼中也沒有找到instance_ method_list_t
或者class_ method_list_t
了嚎,在底層只有方法泪漂。對象方法存在類里面,以此為依據(jù)我們可以大膽假設(shè)歪泳,類方法是否存在元類里面萝勤?
下面我們用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
指針偏移、強轉(zhuǎn)得到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
正在其中呐伞,如此證明類方法確實存在于元類里
敌卓。