五 OC 底層原理 -- isa 的調(diào)用流程

isa 結(jié)構(gòu)回顧

上篇 對(duì)象 與 isa 的關(guān)系 我們得知了 isa 連接了 對(duì)象 和 類(對(duì)象的 isa 指向了 對(duì)象所屬的類對(duì)象)

一. 對(duì)象

當(dāng)我們調(diào)用 obj.class 的時(shí)候?yàn)槭裁磿?huì)返回 類相關(guān)的信息呢? 上篇我們知道 類相關(guān)的信息是存儲(chǔ)在對(duì)象的 isa 中的,那我們是不是可以猜測(cè) obj.class 對(duì) isa 進(jìn)行了相關(guān)的操作 取出了 isa

- (Class)class {
    return object_getClass(self);
}
Class object_getClass(id obj)
{
    if (obj) return obj->getIsa();
    else return Nil;
}
inline Class 
objc_object::getIsa() 
{
    if (fastpath(!isTaggedPointer())) return ISA();

    extern objc_class OBJC_CLASS_$___NSUnrecognizedTaggedPointer;
    uintptr_t slot, ptr = (uintptr_t)this;
    Class cls;

    slot = (ptr >> _OBJC_TAG_SLOT_SHIFT) & _OBJC_TAG_SLOT_MASK;
    cls = objc_tag_classes[slot];
    if (slowpath(cls == (Class)&OBJC_CLASS_$___NSUnrecognizedTaggedPointer)) {
        slot = (ptr >> _OBJC_TAG_EXT_SLOT_SHIFT) & _OBJC_TAG_EXT_SLOT_MASK;
        cls = objc_tag_ext_classes[slot];
    }
    return cls;
}

inline Class 
objc_object::ISA() 
{
    ASSERT(!isTaggedPointer()); 
#if SUPPORT_INDEXED_ISA
    if (isa.nonpointer) {
        uintptr_t slot = isa.indexcls;
        return classForIndex((unsigned)slot);
    }
    return (Class)isa.bits;
#else
    return (Class)(isa.bits & ISA_MASK);
#endif
}

# if __arm64__
#   define ISA_MASK        0x0000000ffffffff8ULL
# elif __x86_64__
#   define ISA_MASK        0x00007ffffffffff8ULL
# else
#   error unknown architecture for packed isa
# endif

可以看到 isa 最終返回的是 (Class)(isa.bits & ISA_MASK); 在 isa.h 可以看到ISA_MASK的定義
所以 當(dāng)我們獲取 class 的時(shí)候 本質(zhì)是通過(guò) isa的內(nèi)存地址 進(jìn)行內(nèi)存運(yùn)算,強(qiáng)轉(zhuǎn) Class 類型 獲得的

二. isa 流程圖

我們知道 對(duì)象的 isa 指向了類捶枢,那么類的 isa 指向的又是? 借用一張 蘋果官方的 isa 流程圖


image.png

我們現(xiàn)在來(lái)驗(yàn)證這幅圖

TObject *obj = [TObject new];

斷點(diǎn)斷住 我們開始打印

image.png

通過(guò)位移運(yùn)算 驗(yàn)證了 obj 的 isa -> TObject(類)

image.png

注意 兩個(gè) TObject 并不是同一個(gè)地址,所以不是同一個(gè)東西
所以這也就是說(shuō) TObject(類) -> TObject(元類)

image.png

我們可以看到元類的isa 經(jīng)過(guò)內(nèi)存計(jì)算拳恋,打印出的是NSObjct也就是我們的根元類
我們可以看到 根元類的地址 和 根元類 isa 指向的地址是同一片地址
所以 也就有了 TObject(元類) -> NSObject(根元類) -> NSObject(根元類) 自己

  • 總結(jié)
  • obj對(duì)象 -> TObect類 -> TObject元類 -> NSObject根元類 -> NSObject根元類自己
    也就有了 官方圖片中的虛線部分

類的結(jié)構(gòu)體

在前面我們知道 isa 是一個(gè) isa_t 的結(jié)構(gòu)體

typedef struct objc_class *Class;
typedef struct objc_object *id;

namespace {
    struct SideTable;
};

#include "isa.h"

union isa_t {
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }

    Class cls;
    uintptr_t bits;
#if defined(ISA_BITFIELD)
    struct {
        ISA_BITFIELD;  // defined in isa.h
    };
#endif
};

之前講到 isa_t 的結(jié)構(gòu)體中 cls 存儲(chǔ)的對(duì)象所屬的類, 所以是Class 又是 objc_class 的結(jié)構(gòu)體指針,也就是說(shuō)類的本質(zhì) 在底層 是 objc_class
然而 objc_class 也繼承于 objc_object, 也就是說(shuō)類也是一個(gè)對(duì)象裆泳,所以第一個(gè)指針也是isa

struct objc_class : objc_object {
    // Class ISA;
    Class superclass;
    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() const {
        return bits.data();
    }
    void setData(class_rw_t *newData) {
        bits.setData(newData);
    }
}
截取了一部分源碼

superclass 父類

所以 第二段地址就是 父類
我們現(xiàn)在開始打印

類的繼承關(guān)系

  • 定義繼承關(guān)系
    TObjectChildren -> TObject -> NSObject
  • 驗(yàn)證類的繼承關(guān)系


    image.png

通過(guò)驗(yàn)證得知 類的繼承關(guān)系 TObjectChildren -> TObject -> NSObject -> nil

元類的繼承關(guān)系

那么元類的繼承關(guān)系又是什么樣的呢

我們用上面類的地址虹蒋,獲取相應(yīng)的元類

// TObjectChildren 的內(nèi)存信息
(lldb) x/4gx obj.class
0x1000082c0: 0x0000000100008298 0x0000000100008270
0x1000082d0: 0x0003000100620150 0x0004802100000000
// TObjectChildren 的元類
(lldb) p/x 0x0000000100008298 & 0x00007ffffffffff8ULL
(unsigned long long) $14 = 0x0000000100008298
(lldb) po 0x0000000100008298
TObjectChildren
// TObjectChildren 的元類的內(nèi)存地址
(lldb) x/4gx 0x0000000100008298
0x100008298: 0x00000002101712a0 0x0000000100008248 -> TObjectChildren 的元類的父類地址
0x1000082a8: 0x00010001007367f0 0x0002e03500000000



//  TObject  內(nèi)存信息
(lldb) x/4gx 0x0000000100008270
0x100008270: 0x0000000100008248 0x00000002101712c8
0x100008280: 0x00000001a7e32f00 0x0000802100000000
// TObject 的元類
(lldb) p/x 0x0000000100008248 & 0x00007ffffffffff8ULL
(unsigned long long) $18 = 0x0000000100008248 -> TObject 的元類地址
(lldb) po 0x0000000100008248
TObject

----
我們從 0x0000000100008248 他是 TObjectChildren 的元類的父類的地址
在下方的TObject的元類地址與其相同
!F旯濉Q丁!2栽凇绝页!判定  TObjectChildren 的元類  繼承于  TObject 的元類
----
TObject 的元類內(nèi)存信息
(lldb) x/4gx 0x0000000100008248
0x100008248: 0x00000002101712a0 0x00000002101712a0->TObject 的元類的父類地址
0x100008258: 0x0001000100620300 0x0002e03500000000

//NSObject 內(nèi)存信息
(lldb) x/4gx 0x00000002101712c8
0x2101712c8: 0x00000002101712a0 0x0000000000000000
0x2101712d8: 0x000100010054a8b0 0x0001801000000000
// NSObject 元類
(lldb) p/x 0x00000002101712a0 & 0x00007ffffffffff8ULL
(unsigned long long) $20 = 0x00000002101712a0-> NSObject 的元類地址
(lldb) po 0x00000002101712a0
NSObject

----
我們從 0x00000002101712a0 他是 TObject 的元類的父類的地址
在下方的NSObject的元類地址與其相同
!<盘瘛P!3跞狻酷鸦!判定  TObject 的元類  繼承于  NSObject 的元類

----

// NSObject 元類的內(nèi)存信息
(lldb) x/4gx 0x00000002101712a0
0x2101712a0: 0x00000002101712a0 0x00000002101712c8
0x2101712b0: 0x0003000100620200 0x0003e03400000000

---
0x00000002101712c8 可以看出 NSObject的元類的父類地址 和 NSObject 的類地址相同
而 NSObject 的父類地址為空
所以 NSObejct的元類 繼承于 NSObject 類 -繼承于 nil
---

通過(guò)一步一步的調(diào)試我們得知
TObjectChildren元類 -> TObject元類 -> NSObject元類 -> NSObject類 -> nil

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子臼隔,更是在濱河造成了極大的恐慌嘹裂,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,692評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件摔握,死亡現(xiàn)場(chǎng)離奇詭異寄狼,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)氨淌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門泊愧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人宁舰,你說(shuō)我怎么就攤上這事拼卵。” “怎么了蛮艰?”我有些...
    開封第一講書人閱讀 162,995評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵腋腮,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我壤蚜,道長(zhǎng)即寡,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,223評(píng)論 1 292
  • 正文 為了忘掉前任袜刷,我火速辦了婚禮聪富,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘著蟹。我一直安慰自己墩蔓,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,245評(píng)論 6 388
  • 文/花漫 我一把揭開白布萧豆。 她就那樣靜靜地躺著奸披,像睡著了一般。 火紅的嫁衣襯著肌膚如雪涮雷。 梳的紋絲不亂的頭發(fā)上阵面,一...
    開封第一講書人閱讀 51,208評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音洪鸭,去河邊找鬼样刷。 笑死,一個(gè)胖子當(dāng)著我的面吹牛览爵,可吹牛的內(nèi)容都是我干的置鼻。 我是一名探鬼主播,決...
    沈念sama閱讀 40,091評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼拾枣,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼沃疮!你這毒婦竟也來(lái)了盒让?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,929評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤司蔬,失蹤者是張志新(化名)和其女友劉穎邑茄,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體俊啼,經(jīng)...
    沈念sama閱讀 45,346評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡肺缕,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,570評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了授帕。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片同木。...
    茶點(diǎn)故事閱讀 39,739評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖跛十,靈堂內(nèi)的尸體忽然破棺而出彤路,到底是詐尸還是另有隱情,我是刑警寧澤芥映,帶...
    沈念sama閱讀 35,437評(píng)論 5 344
  • 正文 年R本政府宣布洲尊,位于F島的核電站,受9級(jí)特大地震影響奈偏,放射性物質(zhì)發(fā)生泄漏坞嘀。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,037評(píng)論 3 326
  • 文/蒙蒙 一惊来、第九天 我趴在偏房一處隱蔽的房頂上張望丽涩。 院中可真熱鬧,春花似錦裁蚁、人聲如沸矢渊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,677評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)昆淡。三九已至,卻和暖如春刽严,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背避凝。 一陣腳步聲響...
    開封第一講書人閱讀 32,833評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工舞萄, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人管削。 一個(gè)月前我還...
    沈念sama閱讀 47,760評(píng)論 2 369
  • 正文 我出身青樓倒脓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親含思。 傳聞我的和親對(duì)象是個(gè)殘疾皇子崎弃,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,647評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容