OC 類的底層實(shí)現(xiàn)

類的底層結(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)吧:

image.png

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


image.png

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é)議列表了


image.png

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


image.png

通過這樣的方式,我們還可以驗(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 方法,所以還是找到了。


image.png
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)的,接下來分解。


image.png

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


類結(jié)構(gòu).png
最后編輯于
?著作權(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ù)。

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