isa補充及類的內(nèi)存分布

準備

需要使用到編譯的objc源碼,objc4可下載词顾,編輯可執(zhí)行程序可參考最新macOS 10.15下objc4-779.1源碼編譯調(diào)試。以下是基于Mac OS平臺運行碱妆,ISA_BITFIELD為在x86架構(gòu)下進行取值肉盹。

isa指向

創(chuàng)建一個LGPerson類的實例化對象person,以16進制打印person地址疹尾,0x0000000100753450是當前對象的地址上忍,x/4gx person 格式化輸出person對象的內(nèi)存骤肛,我們知道首地址為isa,將isa 的地址和ISA_MASK 按位與運算窍蓝,可得到0x00000001000080e8地址腋颠,po該地址得到輸出為LGPerson,從而得到ISA_MASK 0x00007ffffffffff8ULL的作用是舍去低三位和高17位吓笙,從而獲取中間的44位即shiftcls淑玫,在之前我們知道shiftcls是存儲了類指針的值。再次16進制輸出LGPerson類自身時得到地址0x00000001000080e8面睛,由此可知對象的isa指向是類絮蒿。

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        LGPerson *person = [LGPerson alloc];
    }
    return 0;
}

person.png
LGPerson.png

再次格式化輸出LGPerson得到isa,然后將isa & ISA_MASK 按位與計算后得到shiftcls 0x00000001000080c0叁鉴,可以得到輸出也是LGPerson土涝,從而可以得到此時LGPerson為元類。

  • 元類的定義和創(chuàng)建是由編譯器自動完成幌墓,實例對象的isa指向類對象,類對象isa指向元類但壮。作用是將實例方法和類方法進行分區(qū)存放,實例方法存在于類中克锣,類方法存在于元類中
NSObject.png

再次格式化輸出LGPerson得到isa茵肃,然后將isa & ISA_MASK 按位與計算后得到shiftcls 0x000000010034c0f0,可以得到輸出也是NSObject袭祟,然后以16進制打印NSObject.class后得到地址為0x000000010034c140,類型為NSObject的類捞附,此時NSObject被稱為根元類巾乳。從而可以得到如下圖中的流程:

isa及繼承流程.png

  • 從圖中總結(jié)繼承流程:繼承只存在于類中,子類繼承自父類鸟召,父類繼承自根類(NSObject)胆绊,NSObject繼承自nil,類和元類只存在isa指向關(guān)系欧募,不存在繼承關(guān)系压状,子元類繼承自父元類,父元類繼承自根元類跟继,根元類繼承自NSObject种冬。

類的內(nèi)存分布

  • objc_object 是C/C++編寫的結(jié)構(gòu)體 ,是所有類的最低層的實現(xiàn)舔糖,包含了一個isa屬性娱两。
  • objc_class繼承自objc_object是所有自定義類的基礎(chǔ),在objc_class中主要有繼承自objc_objectisa金吗,Class superclass十兢、cache_t cacheclass_data_bits_t bits趣竣,類中的屬性,實例方法和協(xié)議都被保存在bits旱物。

對于類的屬性我們可以使用內(nèi)存平移的方式進行訪問.

struct objc_class : objc_object {
    //  Class ISA;        8字節(jié)
    Class superclass;        8字節(jié)
    cache_t cache;        16字節(jié)
    class_data_bits_t bits; 
    class_rw_t *data()  const {
        return bits.data();
    }
}

以上是ISAsuperclass是容易理解的都是Class類型遥缕,占8字節(jié)。主要就是cache的大小宵呛。

objc_class.png

struct cache_t {
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_OUTLINED
    explicit_atomic<struct bucket_t *> _buckets;
    explicit_atomic<mask_t> _mask;
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
    explicit_atomic<uintptr_t> _maskAndBuckets;
    mask_t _mask_unused;
#if __LP64__
    uint16_t _flags;
#endif
    uint16_t _occupied;

由于static靜態(tài)類型數(shù)據(jù)不再類中单匣,所以cache_t中只有上面一些數(shù)據(jù)占用了內(nèi)存,bucketsbucket_t類型其中的_impunsigned long uintptr_t占用8字節(jié)烤蜕,(unsigned int uint32_t)mask_t類型的_mask封孙。而類中的屬性,實例方法和協(xié)議在class_rw_t中讽营,如下代碼:

struct class_rw_t {

    const method_array_t methods() const {
        auto v = get_ro_or_rwe();
        if (v.is<class_rw_ext_t *>()) {
            return v.get<class_rw_ext_t *>()->methods;
        } else {
            return method_array_t{v.get<const class_ro_t *>()->baseMethods()};
        }
    }

    const property_array_t properties() const {
        auto v = get_ro_or_rwe();
        if (v.is<class_rw_ext_t *>()) {
            return v.get<class_rw_ext_t *>()->properties;
        } else {
            return property_array_t{v.get<const class_ro_t *>()->baseProperties};
        }
    }

    const protocol_array_t protocols() const {
        auto v = get_ro_or_rwe();
        if (v.is<class_rw_ext_t *>()) {
            return v.get<class_rw_ext_t *>()->protocols;
        } else {
            return protocol_array_t{v.get<const class_ro_t *>()->baseProtocols};
        }
    }
}

從而得到class_rw_t中包含了methods虎忌、propertiesprotocols屬性,所以可以看出關(guān)于類中的屬性橱鹏,方法和協(xié)議都在class_rw_t *data()中膜蠢。

拓展

objc_object對象的關(guān)系

對象是以objc_object為模板創(chuàng)建而來的,NSObject為OC層結(jié)構(gòu)莉兰,objc_objectC/C++層的結(jié)構(gòu)體挑围。

指針平移
 int a[4] = {1, 2, 3, 4};
  int *b = a;
  for (int i = 0; i < 4; i++) {
      NSLog(@"索引取值:%d", a[i]);
      NSLog(@"指針取值:%d", *(b+i));
  }

聲明一個int數(shù)組a,聲明一個指針b指向數(shù)組a糖荒,然后分別使用數(shù)組下標和指針來輸出數(shù)據(jù)杉辙,得出如下結(jié)果:

指針運算.png

因此我們可以看出在輸出數(shù)組數(shù)據(jù)時我們既可以使用下標輸出,也可以使用指針來輸出捶朵。在代碼中蜘矢,*b = a的含義是指針b指向了數(shù)組a的首地址,即數(shù)組a的第一個元素所在的地址综看,因此只需要移動指針品腹,就可以得到整個數(shù)組的元素。由此可以類推到類中红碑。
指針平移.png

當我們使用p/x 打印類類型和地址舞吭,該地址即為首地址,所以我們可以使用內(nèi)存平移的方式來訪問類的內(nèi)存情況析珊。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末羡鸥,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子唾琼,更是在濱河造成了極大的恐慌兄春,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,542評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件锡溯,死亡現(xiàn)場離奇詭異赶舆,居然都是意外死亡哑姚,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,596評論 3 385
  • 文/潘曉璐 我一進店門芜茵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來叙量,“玉大人,你說我怎么就攤上這事九串〗逝澹” “怎么了?”我有些...
    開封第一講書人閱讀 158,021評論 0 348
  • 文/不壞的土叔 我叫張陵猪钮,是天一觀的道長品山。 經(jīng)常有香客問我,道長烤低,這世上最難降的妖魔是什么肘交? 我笑而不...
    開封第一講書人閱讀 56,682評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮扑馁,結(jié)果婚禮上涯呻,老公的妹妹穿的比我還像新娘。我一直安慰自己腻要,他們只是感情好复罐,可當我...
    茶點故事閱讀 65,792評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著雄家,像睡著了一般效诅。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上趟济,一...
    開封第一講書人閱讀 49,985評論 1 291
  • 那天填帽,我揣著相機與錄音,去河邊找鬼咙好。 笑死,一個胖子當著我的面吹牛褐荷,可吹牛的內(nèi)容都是我干的勾效。 我是一名探鬼主播,決...
    沈念sama閱讀 39,107評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼叛甫,長吁一口氣:“原來是場噩夢啊……” “哼层宫!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起其监,我...
    開封第一講書人閱讀 37,845評論 0 268
  • 序言:老撾萬榮一對情侶失蹤萌腿,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后抖苦,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體毁菱,經(jīng)...
    沈念sama閱讀 44,299評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡米死,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,612評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了贮庞。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片峦筒。...
    茶點故事閱讀 38,747評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖窗慎,靈堂內(nèi)的尸體忽然破棺而出物喷,到底是詐尸還是另有隱情,我是刑警寧澤遮斥,帶...
    沈念sama閱讀 34,441評論 4 333
  • 正文 年R本政府宣布峦失,位于F島的核電站,受9級特大地震影響术吗,放射性物質(zhì)發(fā)生泄漏尉辑。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,072評論 3 317
  • 文/蒙蒙 一藐翎、第九天 我趴在偏房一處隱蔽的房頂上張望材蹬。 院中可真熱鬧,春花似錦吝镣、人聲如沸堤器。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,828評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽闸溃。三九已至,卻和暖如春拱撵,著一層夾襖步出監(jiān)牢的瞬間辉川,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,069評論 1 267
  • 我被黑心中介騙來泰國打工拴测, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留乓旗,地道東北人。 一個月前我還...
    沈念sama閱讀 46,545評論 2 362
  • 正文 我出身青樓集索,卻偏偏與公主長得像屿愚,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子务荆,可洞房花燭夜當晚...
    茶點故事閱讀 43,658評論 2 350