ios 類的結(jié)構(gòu)分析

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,也就是類中氯庆。類方法存在元類中蹭秋。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末扰付,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子仁讨,更是在濱河造成了極大的恐慌羽莺,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件洞豁,死亡現(xiàn)場離奇詭異盐固,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)丈挟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進(jìn)店門刁卜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人曙咽,你說我怎么就攤上這事蛔趴。” “怎么了例朱?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵孝情,是天一觀的道長。 經(jīng)常有香客問我洒嗤,道長箫荡,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任渔隶,我火速辦了婚禮菲茬,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘派撕。我一直安慰自己婉弹,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布终吼。 她就那樣靜靜地躺著镀赌,像睡著了一般。 火紅的嫁衣襯著肌膚如雪际跪。 梳的紋絲不亂的頭發(fā)上商佛,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天,我揣著相機(jī)與錄音姆打,去河邊找鬼良姆。 笑死,一個胖子當(dāng)著我的面吹牛幔戏,可吹牛的內(nèi)容都是我干的玛追。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼痊剖!你這毒婦竟也來了韩玩?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤陆馁,失蹤者是張志新(化名)和其女友劉穎找颓,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體叮贩,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡击狮,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了益老。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片彪蓬。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖杨箭,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情储狭,我是刑警寧澤互婿,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站辽狈,受9級特大地震影響慈参,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜刮萌,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一驮配、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧着茸,春花似錦壮锻、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至敬特,卻和暖如春掰邢,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背伟阔。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工辣之, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人皱炉。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓怀估,卻偏偏與公主長得像,于是被迫代替她去往敵國和親合搅。 傳聞我的和親對象是個殘疾皇子奏夫,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,979評論 2 355