OC類(lèi)結(jié)構(gòu)分析

類(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)型

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市渊鞋,隨后出現(xiàn)的幾起案子绰更,更是在濱河造成了極大的恐慌,老刑警劉巖锡宋,帶你破解...
    沈念sama閱讀 219,366評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件儡湾,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡执俩,警方通過(guò)查閱死者的電腦和手機(jī)徐钠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)役首,“玉大人丹皱,你說(shuō)我怎么就攤上這事∷嗡埃” “怎么了摊崭?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,689評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)杰赛。 經(jīng)常有香客問(wèn)我呢簸,道長(zhǎng),這世上最難降的妖魔是什么乏屯? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,925評(píng)論 1 295
  • 正文 為了忘掉前任根时,我火速辦了婚禮,結(jié)果婚禮上辰晕,老公的妹妹穿的比我還像新娘蛤迎。我一直安慰自己,他們只是感情好含友,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,942評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布替裆。 她就那樣靜靜地躺著校辩,像睡著了一般。 火紅的嫁衣襯著肌膚如雪辆童。 梳的紋絲不亂的頭發(fā)上宜咒,一...
    開(kāi)封第一講書(shū)人閱讀 51,727評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音把鉴,去河邊找鬼故黑。 笑死,一個(gè)胖子當(dāng)著我的面吹牛庭砍,可吹牛的內(nèi)容都是我干的场晶。 我是一名探鬼主播,決...
    沈念sama閱讀 40,447評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼怠缸,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼诗轻!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起凯旭,我...
    開(kāi)封第一講書(shū)人閱讀 39,349評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤概耻,失蹤者是張志新(化名)和其女友劉穎使套,沒(méi)想到半個(gè)月后罐呼,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,820評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡侦高,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,990評(píng)論 3 337
  • 正文 我和宋清朗相戀三年嫉柴,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片奉呛。...
    茶點(diǎn)故事閱讀 40,127評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡计螺,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出瞧壮,到底是詐尸還是另有隱情登馒,我是刑警寧澤,帶...
    沈念sama閱讀 35,812評(píng)論 5 346
  • 正文 年R本政府宣布咆槽,位于F島的核電站陈轿,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏秦忿。R本人自食惡果不足惜麦射,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,471評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望灯谣。 院中可真熱鬧潜秋,春花似錦、人聲如沸胎许。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,017評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至杀饵,卻和暖如春莽囤,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背切距。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,142評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工朽缎, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人谜悟。 一個(gè)月前我還...
    沈念sama閱讀 48,388評(píng)論 3 373
  • 正文 我出身青樓话肖,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親葡幸。 傳聞我的和親對(duì)象是個(gè)殘疾皇子最筒,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,066評(píng)論 2 355