類的底層結(jié)構(gòu)
和對(duì)象的底層一樣,類對(duì)象前八個(gè)字節(jié)也是存的isa指針,那么第二個(gè)字節(jié)表示什么,我們可以直接找到源碼,查看類結(jié)構(gòu)。
- 很明顯,第一個(gè)指針是isa(注釋掉,隱藏著的)
- 第二個(gè)是superclass
- 第三個(gè)cache,每一個(gè)類都會(huì)有一個(gè)cache,用來存儲(chǔ)調(diào)用過的方法等,增加性能
-
第四個(gè)最重要,bits,里面有我們類中的主要組成部分,看下面返回了data,接下來我們就看看data中有什么東東
image.png
class_rw_t的結(jié)構(gòu)
bits中返回了data,data的結(jié)構(gòu)是class_rw_t,我們查看其中是什么樣的結(jié)構(gòu),不進(jìn)去不知道,一進(jìn)去嚇一跳,里面全是我們這個(gè)類中重要的信息。
調(diào)試時(shí)怎么進(jìn)去呢?
通過類對(duì)象執(zhí)行偏移進(jìn)去的,想要進(jìn)去data中的部分,將類對(duì)象指針向前偏移兩位就可以了。
先看看其中結(jié)構(gòu)吧:

沒錯(cuò),這里面有方法列表,屬性列表,協(xié)議列表。
這里需要注意一下,readonly的屬性和相關(guān)不可更改的都存在這里,這里的部分是不能進(jìn)行修改的,當(dāng)一個(gè)類創(chuàng)建完成的時(shí)候。
所以,類創(chuàng)建完成后,是不能添加ivar的了!

LLDB調(diào)試驗(yàn)證
看了源碼中的代碼,現(xiàn)在我們直接通過LLDB來調(diào)試驗(yàn)證正確性。
在已經(jīng)導(dǎo)入源碼的情況下,調(diào)試可以拿到底層數(shù)據(jù)結(jié)構(gòu)中的對(duì)象。
1、拿到類對(duì)象,指針偏移兩位,獲取到data
2、打印data中所有數(shù)據(jù),你能看到這個(gè)類的基本方法列表,屬性列表和協(xié)議列表了

繼續(xù)往里走,可以拿到我們自己創(chuàng)建的方法

通過這樣的方式,我們還可以驗(yàn)證兩個(gè)東西:
- 類方法存在元類中
- 實(shí)例方法存在類中
打印驗(yàn)證方法的獲取
上面我們能夠通過LLDB直接查看方法存的位置,類方法存在元類中,實(shí)例方法存在類中。(0表示有,1表示沒有)
LGPerson中書寫兩個(gè)方法,并實(shí)現(xiàn):
- (void)lg_instanceMethod;
+ (void)lg_classMethod;
demo1:class_getInstanceMethod
Method method1 = class_getInstanceMethod([LGPerson class], @selector(lg_instanceMethod)); // 類對(duì)象 拿 實(shí)例方法
Method method2 = class_getInstanceMethod(objc_getMetaClass("LGPerson"), @selector(lg_instanceMethod)); // 元類對(duì)象 拿 實(shí)例方法
Method method3 = class_getInstanceMethod([LGPerson class], @selector(lg_classMethod));
Method method4 = class_getInstanceMethod(objc_getMetaClass("LGPerson"), @selector(lg_classMethod));
NSLog(@"%p-%p-%p-%p",method1,method2,method3,method4);
這里輸出:1 0 0 1
解釋:
對(duì)象方法是存在類中的,method1為1,method2為0;
類方法存在元類中的,method3為0,method4為1。
demo2:class_getMethodImplementation
IMP imp1 = class_getMethodImplementation([LGPerson class], @selector(lg_instanceMethod));
IMP imp2 = class_getMethodImplementation([LGPerson class], @selector(lg_classMethod));
IMP imp3 = class_getMethodImplementation(objc_getMetaClass("LGPerson"), @selector(lg_instanceMethod));
IMP imp4 = class_getMethodImplementation(objc_getMetaClass("LGPerson"), @selector(lg_classMethod));
NSLog(@"%p-%p-%p-%p",imp1,imp2,imp3,imp4);
這里輸出:1 1 1 1
解釋:imp1 和 imp4與上面同理。為什么imp2 和 imp3也會(huì)有呢?其實(shí)類中并沒有類方法,元類中也沒有對(duì)象方法,只是 class_getMethodImplementation 底層會(huì)走 _objc_msgForward 方法,所以還是找到了。

demo3:class_getClassMethod
Method method1 = class_getClassMethod([LGPerson class], @selector(lg_instanceMethod));
Method method2 = class_getClassMethod(objc_getMetaClass("LGPerson"), @selector(lg_instanceMethod));
// 類 --> lg_classMethod 類方法
// 元類
Method method3 = class_getClassMethod([LGPerson class], @selector(lg_classMethod));
Method method4 = class_getClassMethod(objc_getMetaClass("LGPerson"), @selector(lg_classMethod));
NSLog(@"%p-%p-%p-%p",method1,method2,method3,method4);
輸出:0 0 1 1
解釋:我們從底層源碼中看出,class_getClassMethod 底層使用就是 class_getInstanceMethod
Method class_getClassMethod(Class cls, SEL sel)
{
if (!cls || !sel) return nil;
return class_getInstanceMethod(cls->getMeta(), sel);
}
所以 method1 和 method2 中根本沒有 lg_instanceMethod 的類方法,lg_instanceMethod 是一個(gè)對(duì)象方法。
method3 和 method4 其實(shí)都是從元類中找尋類方法,所以當(dāng)然能找到。
添加方法,協(xié)議,屬性 attachLists
我們的類,在創(chuàng)建類和需要添加的時(shí)候都會(huì)調(diào)用 attachLists 方法,將我們需要添加的方法,協(xié)議,屬性添加進(jìn)去。具體 attachLists 是怎么實(shí)現(xiàn)的,接下來分解。

最后附上一張類的底層結(jié)構(gòu)圖:

