02--對(duì)象本質(zhì)04--類的結(jié)構(gòu)

[TOC]

  1. 怎么讀到cache_t屬性


    image

四個(gè)屬性

在源碼中關(guān)于類objc_class的結(jié)構(gòu)體代碼, 總共有200多行, 而我們最關(guān)注的只有上述四個(gè)屬性

類結(jié)構(gòu)源碼
  1. // Class ISA;: 8字節(jié)

    • isa屬性是繼承與父類的屬性, 所以是在屬性的第一個(gè)位置, 表示isa指針
    • 指針占8個(gè)字節(jié)
  2. Class superclass;: 8字節(jié)

    • 除了isa屬性之外, 父類指針就是第二個(gè)位置, 表示父類的isa指針
    • 指針占8個(gè)字節(jié)
  3. cache_t cache;: 16字節(jié)

    struct cache_t {
    struct bucket_t *_buckets;  // 8
    mask_t _mask;               // 4
    mask_t _occupied;           // 4
    

    我們可以先看看cache_t的結(jié)構(gòu), 根據(jù)內(nèi)存對(duì)齊的規(guī)則, 可以計(jì)算出這個(gè)結(jié)構(gòu)體的大小為 16字節(jié)

    • cache_t結(jié)構(gòu)體
    • 16字節(jié)
  4. class_data_bits_t bits;: 所有的數(shù)據(jù)存儲(chǔ)位置, 重點(diǎn)研究對(duì)象

探索四個(gè)屬性

1. 準(zhǔn)備類

@interface LGPerson : NSObject
{
NSString *_hobby;
}

@property (nonatomic, copy) NSString *lgName;

@end
  • 定義一個(gè)成員變量 _hobby
  • 定義一個(gè)屬性 lgName
  • 我們知道編譯器會(huì)自動(dòng)生成一個(gè) lgName 的getter和setter方法和一個(gè)帶下劃線的成員變量 _lgName

2. 查看 class_data_bits_t bits 屬性

根據(jù)上面對(duì)objc_class的四個(gè)屬性的分析, class_data_bits_t bits是在第四個(gè)屬性的位置, 但不能僅根據(jù)屬性字段的第四個(gè)位置的地址來輸出它, 需要根據(jù)地址偏移來完成.
首地址偏移32位, 可以找到 bits 屬性的地址,

p (class_data_bits_t *)(0x1000039a0+32)

class_data_bits_t

你肯定會(huì)有一個(gè)疑問, 為什么這里要強(qiáng)轉(zhuǎn)成 class_data_bits_t * 而不是class_data_bits_t.

我們來看下面一行代碼

class_rw_t *data() { 
    return bits.data();
}

data返回的是是一個(gè)指針類的數(shù)據(jù), 我們可以猜測(cè)這里有一大坨的數(shù)據(jù), 怎么讀取這一坨數(shù)據(jù), 自然就是我們的指針了, 可能有其他理論依據(jù), 但這里作為一個(gè)探索分析就只能這么分析了。

【注意】在我們自己的iOS APP工程里面是無法分析的,原因是因?yàn)闆]有導(dǎo)入objc源碼,所以無法找到 class_data_bits_t 類。

3. 查看 class_rw_t bits.data() 數(shù)據(jù)源

上面的截圖中可以知道, 1表示 `bits`的指針, 那么就可以通過 `1` 來讀取

p $1->data()

data

4. 分析 class_rw_t 結(jié)構(gòu)

struct class_rw_t {
    // Be warned that Symbolication knows the layout of this structure.
    uint32_t flags;
    uint32_t version;

    const class_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
...

在這里看到了一個(gè)class_ro_t *ro, 不知道是什么, 但是既然是作為指針出現(xiàn)在這里, 一定是有它的意義的, 后面再做分析.
除了ro, 還有比較熟悉的幾個(gè)屬性,

  • method_array_t methods: 方法數(shù)組
  • property_array_t properties: 屬性數(shù)組
  • protocol_array_t protocols: 協(xié)議數(shù)組

在上面的截圖中 (class_rw_t *) $2 = 0x0000000101e68ef0, $2表示的是, class_rw_t *的指針, 那么可以嘗試輸出這個(gè)指針的值

p *$2

class_rw_t

5. 同樣的方式分析ro

找到 ro 的指針
p $3.ro
打印 ro 的信息
p *$4

ro

6. 探索LGPerson中的數(shù)據(jù)

  • 成員變量 ivar

    找到 ivars 的指針
    $4->ivars
    打印 ivars 的信息
    p *$6

    ivar_list_t

    ivar_list_t中有個(gè)count = 2, 而我們知道我們定義的成員變量個(gè)數(shù)剛好為2,這里只輸出了第一個(gè), 所以下面要輸出全部的成員變量.

    p $7.get(1)

    ivars
  • 方法 method
    同樣的操作, 找到了編譯器為我們自動(dòng)生成的getter和setter方法


    method
  • 屬性 properties
    同樣的操作, 找到了lgName存在的地方


    properties

總結(jié)

這篇文章記錄的是怎么探索類的結(jié)構(gòu),我們常用的數(shù)據(jù)都是在哪里、怎么存儲(chǔ)的,沒有對(duì)加載時(shí)機(jī)做分析(也即什么時(shí)候加載到這些地方),后面會(huì)講。

探索完類的結(jié)構(gòu)之后,有一種豁然開朗的感覺,以前都是靠猜測(cè)來分析屬性、方法等存在哪里,現(xiàn)在能從內(nèi)存中取出來,相當(dāng)于是打開了潘多拉魔盒。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容