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

1. 類的初探

isa結(jié)構(gòu)解析中,自定義LSPerson 類繼承自NSObject,重寫成C++代碼如下

struct LSPerson_IMPL {
    struct NSObject_IMPL NSObject_IVARS;
};

struct NSObject_IMPL結(jié)構(gòu)體定義如下

struct NSObject_IMPL {
    Class isa;
};

typedef struct objc_class *Class;
Class為指向 結(jié)構(gòu)體struct objc_class的指針佛嬉,所以繼承自NSObject的對(duì)象中都有isa成員變量。那么NSObject的isa來自何處呢定页?

objc_class源碼如下

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();
    }

    ...省略的函數(shù)
}

由此可以知道struct objc_class繼承自struct objc_object

struct objc_object源碼如下

struct objc_object {
private:
    isa_t isa;

public:
    ...省略的函數(shù)
}

總結(jié):在OC層梦湘,對(duì)象的isa指向?qū)ο蟮念愃澳龋瑢?duì)象的類繼承自NSObject魁兼,NSObject是以結(jié)構(gòu)體struct objc_class為模版創(chuàng)建的婉徘;在C++層,struct objc_class繼承自struct objc_object咐汞,struct objc_object內(nèi)部有isa成員變量盖呼。

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

由結(jié)構(gòu)體struct objc_class源碼可知,類的信息存儲(chǔ)在class_data_bits_t bits;中化撕。
為了取到class_data_bits_t bits;几晤,需要在結(jié)構(gòu)體struct objc_class進(jìn)行地址偏移計(jì)算。

  1. Class ISA; //結(jié)構(gòu)體指針類型 8字節(jié)
  2. Class superclass;//結(jié)構(gòu)體指針類型 8字節(jié)
  3. cache_t cache;

cache_t源碼

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;

    ...其他方法

cache_t結(jié)構(gòu)體計(jì)算:explicit_atomic<uintptr_t> _maskAndBuckets; long類型 8字節(jié)植阴; mask_t _mask_unused; int類型 4字節(jié)蟹瘾; uint16_t _flags; short類型 2字節(jié);uint16_t _occupied; short類型 2字節(jié)掠手。根據(jù)內(nèi)存對(duì)齊原則cache_t大小為16字節(jié)热芹。故為了取到class_data_bits_t bits;,需要在結(jié)構(gòu)體struct objc_class對(duì)首地址進(jìn)行偏移32字節(jié)惨撇。

探索流程:

//獲取LSPerson類首地址
(lldb) p/x LSPerson.class
(Class) $0 = 0x00000001000021c8 LSPerson

//獲取類isa中對(duì)內(nèi)存信息
(lldb) x/4gx 0x00000001000021c8
(lldb) x/4gx 0x00000001000021c8
0x1000021c8: 0x00000001000021a0 0x00007fff944ae118
0x1000021d8: 0x00007fff6cd18140 0x0000801000000000

//根據(jù)地址偏移0x1000021c8 -> 0x1000021d8 獲取bits信息
(lldb) p (class_data_bits_t *)0x1000021d8
(class_data_bits_t *) $1 = 0x00000001000021e8

//根據(jù)struct objc_class中data()函數(shù)獲取class_rw_t
(lldb) p $1->data()
(class_rw_t *) $2 = 0x00000001010224b0

(lldb) p *($2)
(class_rw_t) $4 = {
  flags = 2148007936
  witness = 0
  ro_or_rw_ext = {
    std::__1::atomic<unsigned long> = 4294975744
  }
  firstSubclass = nil
  nextSiblingClass = NSUUID
}

(lldb) p $4.methods()      //打印出對(duì)象方法列表
(lldb) p $4.properties()    //打印出屬性列表
(lldb) p $4.protocols()     //打印出協(xié)議列表

class_rw_t源碼

struct class_rw_t {
    // Be warned that Symbolication knows the layout of this structure.
    uint32_t flags;
    uint16_t witness;
#if SUPPORT_INDEXED_ISA
    uint16_t index;
#endif

    explicit_atomic<uintptr_t> ro_or_rw_ext;

    Class firstSubclass;
    Class nextSiblingClass;

private: 
    ...省去其他函數(shù)

public: 
    ...省去其他函數(shù)
    const class_ro_t *ro() const {
        auto v = get_ro_or_rwe();
        if (slowpath(v.is<class_rw_ext_t *>())) {
            return v.get<class_rw_ext_t *>()->ro;
        }
        return v.get<const class_ro_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};
        }
    }
};

根據(jù)已獲取的class_rw_t,調(diào)用結(jié)構(gòu)體內(nèi)部函數(shù)methods()府寒、properties()魁衙、protocols()即可獲取類的方法列表报腔,屬性列表以及協(xié)議列表。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末剖淀,一起剝皮案震驚了整個(gè)濱河市纯蛾,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌纵隔,老刑警劉巖翻诉,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異捌刮,居然都是意外死亡碰煌,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門绅作,熙熙樓的掌柜王于貴愁眉苦臉地迎上來玷氏,“玉大人锡足,你說我怎么就攤上這事。” “怎么了世落?”我有些...
    開封第一講書人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長匾乓。 經(jīng)常有香客問我懒震,道長,這世上最難降的妖魔是什么岂贩? 我笑而不...
    開封第一講書人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任茫经,我火速辦了婚禮,結(jié)果婚禮上河闰,老公的妹妹穿的比我還像新娘科平。我一直安慰自己,他們只是感情好姜性,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開白布瞪慧。 她就那樣靜靜地躺著,像睡著了一般部念。 火紅的嫁衣襯著肌膚如雪弃酌。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評(píng)論 1 301
  • 那天儡炼,我揣著相機(jī)與錄音妓湘,去河邊找鬼。 笑死乌询,一個(gè)胖子當(dāng)著我的面吹牛榜贴,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播妹田,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼唬党,長吁一口氣:“原來是場噩夢啊……” “哼鹃共!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起驶拱,我...
    開封第一講書人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤霜浴,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后蓝纲,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體阴孟,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年税迷,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了永丝。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡翁狐,死狀恐怖类溢,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情露懒,我是刑警寧澤闯冷,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站懈词,受9級(jí)特大地震影響蛇耀,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜坎弯,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一纺涤、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧抠忘,春花似錦撩炊、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至囚灼,卻和暖如春骆膝,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背灶体。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來泰國打工阅签, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蝎抽。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓政钟,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子养交,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354