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

-
// Class ISA;: 8字節(jié)- isa屬性是繼承與父類的屬性, 所以是在屬性的第一個(gè)位置, 表示isa指針
- 指針占8個(gè)字節(jié)
-
Class superclass;: 8字節(jié)- 除了isa屬性之外, 父類指針就是第二個(gè)位置, 表示父類的isa指針
- 指針占8個(gè)字節(jié)
-
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é)
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)

你肯定會(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` 來讀取
p $1->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

5. 同樣的方式分析ro
找到 ro 的指針
p $3.ro
打印 ro 的信息
p *$4

6. 探索LGPerson中的數(shù)據(jù)
-
成員變量 ivar
找到 ivars 的指針
$4->ivars
打印 ivars 的信息
p *$6ivar_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)于是打開了潘多拉魔盒。




