Class 的本質(zhì)是 objc_class類型的結(jié)構(gòu)體, objc_class本質(zhì)也是對(duì)象,繼承自objc_object。
objc_class 內(nèi)部也有一個(gè)isa指針。

類對(duì)象的isa指向分析
我們打印一下對(duì)象的指針來查看一下

我們將p的指針與上掩碼,得到了類對(duì)象LGPerson,我們通過類對(duì)象的指針與上排碼能得到什么呢?我們發(fā)現(xiàn)類對(duì)象的isa指針也指向LGPerson,同時(shí)2個(gè)LGPerson的內(nèi)存地址不一樣。

我們發(fā)現(xiàn)對(duì)象isa所指向的LGPerson類的地址為0x00000001000082f0,而LGPerson類中isa所指向的LGPerson地址為0x00000001000082c8,說明這是兩個(gè)不同的類!而且LGPerson的元類也是LGPerson。
到此我們得到了如下結(jié)論
實(shí)例對(duì)象的isa -->類對(duì)象 isa--> 元類對(duì)象
那元類的isa指針指向哪里呢,我們接著看

通過打印看到,元類的指針指向根元類
那么根元類的isa又指向哪里呢?

通過打印根元類的內(nèi)存空間,發(fā)現(xiàn)根元類的isa指向了自己。
總結(jié)如圖

類與元類的繼承關(guān)系
先說結(jié)論:元類的父類就是父類的元類

在進(jìn)行superclass走位分析之前,先要確定superclass指針?biāo)谖恢?,查看objc_class源碼實(shí)現(xiàn):
struct objc_class : objc_object {
// Class ISA;
Class superclass; // 第二個(gè)8字節(jié)
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指針在類的第二個(gè)8字節(jié)中。

繼續(xù)打印LGperson的父類

通過打印發(fā)現(xiàn)LGPerson的父類指向NSObject
繼續(xù) 查找NSObject的父類

通過驗(yàn)證可以確定,NSObject的isa 指向自己 , NSObject的父類是nil。
總結(jié)如圖

那有沒有想過蘋果為什么要設(shè)計(jì)元類呢?這個(gè)我們放在后面講,這邊我們先看一下objc_class里存放了什么東西
通過內(nèi)存平移訪問bits數(shù)據(jù)
內(nèi)存平移

如圖中所示,p是個(gè)指針,指向了整形數(shù)組的首地址c,d+1則表示指針平移int類型大小的字節(jié),即就是平移4個(gè)字節(jié),也就是數(shù)組第一個(gè)元素的地址,以此類推。
好了,知道了什么是內(nèi)存平移,我們來對(duì)這個(gè)bits進(jìn)行探究。我們定義一個(gè)Person類,

運(yùn)行程序,并且在main函數(shù)中打上斷點(diǎn),先輸出Person類對(duì)象的地址分布,參考源碼中objc_class的結(jié)構(gòu)(全局搜索struct objc_class),要拿到bits,需要內(nèi)存平移32位,也就是說0x1000081b8就是我們要操作的地址。我們將該地址進(jìn)行強(qiáng)轉(zhuǎn)為class_data_bits_t得到一個(gè)地址,我們通過對(duì)地址進(jìn)行取值,得到的結(jié)果對(duì)我們并沒有什么參考意義,怎么辦呢?

結(jié)果返回的class_data_bits_t類型我們可以試著對(duì)其進(jìn)行探索,跳轉(zhuǎn)到其定義處:
struct class_data_bits_t {
friend objc_class;
// Values are the FAST_ flags above.
uintptr_t bits;
public:
// !!重點(diǎn)--獲取class_rw_t!!
class_rw_t* data() const {
return (class_rw_t *)(bits & FAST_DATA_MASK);
}
...
// !!重點(diǎn)--獲取class_ro_t!!
const class_ro_t *safe_ro() const {
class_rw_t *maybe_rw = data();
if (maybe_rw->flags & RW_REALIZED) {
// maybe_rw is rw
return maybe_rw->ro();
} else {
// maybe_rw is actually ro
return (class_ro_t *)maybe_rw;
}
}
發(fā)現(xiàn)其有個(gè)叫做class_rw_t* data()的方法,我們試著對(duì)其進(jìn)行調(diào)用并得到的地址進(jìn)行取值:

好像沒什么對(duì)我們有用的信息,這時(shí)候我們就跳轉(zhuǎn)到class_rw_t類型的定義處,發(fā)現(xiàn)里面有很多方法,我們發(fā)現(xiàn)了幾個(gè)很熟悉的方法:methods,properties,protocols等等。我們試著對(duì)其進(jìn)行調(diào)用,里面有個(gè)list,我們繼續(xù)對(duì)其進(jìn)行解析,得到一個(gè)method_list_t類型的地址,并對(duì)其取值。


上面的輸出信息里有個(gè)count,我們猜測是實(shí)例方法的個(gè)數(shù),我們定義的類中有兩個(gè)屬性,setter和getter方法加起來有4個(gè),再加上定義的一個(gè)instanceMethod方法,總共有5個(gè),可是這個(gè)count卻有6個(gè)。別急,我們接著往下看。
我們查看method_list_t的定義,沒找到有用的信息,去entsize_list_tt里面找:


這個(gè)get方法應(yīng)該就是獲取method的方法,給其傳下標(biāo)即可。我們可以試下,發(fā)現(xiàn)其輸出結(jié)果并不是我們預(yù)想的那樣。

那我們接著對(duì)method_t類型進(jìn)行探索,跳轉(zhuǎn)到其定義處,發(fā)現(xiàn)其有一個(gè)getDescription方法:

我們試著對(duì)其進(jìn)行調(diào)用并對(duì)所得地址進(jìn)行取值:

我們成功拿到了方法名以及參數(shù)列表。如法炮制,對(duì)剩下的五個(gè)方法進(jìn)行打?。?/p>

原來多出的那個(gè)方法是這個(gè)叫.cxx_destruct的方法,猜測是反初始化方法。
我們也可以通過上面的探索的方式獲取屬性列表。
所以類對(duì)象中存儲(chǔ)了實(shí)例方法列表,屬性列表以及協(xié)議方法列表以及cache和指向元類的指針以及指向父類的指針。