分類的加載原理

上一篇文章中,我們分析了類的加載,并且已經(jīng)摸到了分類的信息,那么是怎么加到我們類里面去的呢,還有在什么時(shí)候加的呢,這是本篇文章將進(jìn)行探討的

分類怎么加載到類里面的

我們接著上一篇文章的結(jié)尾講, 在attachCategories里面拿到分類信息進(jìn)行排序完了之后會(huì)就來到

if (mcount > 0) {
  prepareMethodLists(cls, mlists + ATTACH_BUFSIZ - mcount, mcount, NO, fromBundle);
   rwe->methods.attachLists(mlists + ATTACH_BUFSIZ - mcount, mcount);
   if (flags & ATTACH_EXISTING) flushCaches(cls);
}

我們先來看 attachLists,點(diǎn)到源碼里面去

attachLists三種情況

在這個(gè)里面分為三種情況:

首先第一種情況: 0 lists -> 1 list,很簡單,就是從0添加到1的一個(gè)過程。第一種情況用于添加本類

再來看到第二種情況: newCount = oldCount(1) + addedCount 代表原來的容量加上添加的容量之和,拿到新的容量大小之后開辟了一個(gè)newCount大小的數(shù)組,然后把oldList放在addedLists的后面。第二種情況用于添加第一個(gè)分類

第三種情況: 多個(gè)lists,前面的步驟跟第二種情況是一樣的,區(qū)別就在于先添加老的數(shù)組。 老數(shù)組添加的位置是以新數(shù)組數(shù)量為開始位置,新數(shù)組的開始位置就是起始位置, 相當(dāng)于舊的數(shù)組放在新數(shù)組的后面,越晚加進(jìn)來的,越在前面。第三種情況用于添加多個(gè)分類

為什么把分類數(shù)組放在前面,還有就是越晚加進(jìn)來的越放在前面,因?yàn)槲覀兎诸惱锩娴姆椒ㄊ莾?yōu)先調(diào)用的,所以得加在前面,而且越晚加進(jìn)來的分類越先調(diào)用。

說了這么多,我們通過斷點(diǎn)調(diào)試驗(yàn)證一下這三種情況,首先來到attachCategories里面的rwe創(chuàng)建

初始化rwe
創(chuàng)建rwe

來到rwe創(chuàng)建的地方,初始化后我們打印出rwe為空,然后拿到本類的methodList進(jìn)行rwe->methods.attachLists(&list, 1);, 我們點(diǎn)到attachLists里面打個(gè)斷點(diǎn)看會(huì)不會(huì)來到第一種情況

本類來到第一種情況

本類LGPerson確實(shí)來到了第一種情況,進(jìn)來將LGPerson里面的baseMethods賦值到了rwe里面的methods,此時(shí)rwe就有值了

打印rwe

同樣的,再來驗(yàn)證下第二種情況和第三種情況,前面說過的第二種情況是添加第一個(gè)分類,第三種情況是添加多個(gè)分類

在添加分類的時(shí)候,首先通過指針地址的平移拿到要添加的分類

if (mcount > 0) {
        prepareMethodLists(cls, mlists + ATTACH_BUFSIZ - mcount, mcount, NO, fromBundle);
        rwe->methods.attachLists(mlists + ATTACH_BUFSIZ - mcount, mcount);
        if (flags & ATTACH_EXISTING) flushCaches(cls);
    }

平移到要添加的分類之后傳給attachLists里面的addedLists進(jìn)行添加

第一個(gè)分類來到第二種情況

第二種情況也得以驗(yàn)證, 接下來再來看看LGB會(huì)不會(huì)來到第三種情況

第二個(gè)分類來到第三種情況

最后我們來調(diào)用一下kc_instanceMethod1 ,看看是不是調(diào)用LGB分類里的方法,因?yàn)槲覀兦懊嬲f過越晚加進(jìn)來的,越在前面,越先調(diào)用

調(diào)用kc_instanceMethod1

完美,到目前為止我們已經(jīng)驗(yàn)證了分類是如何加載到類的,那么接下來再來看在什么時(shí)候加進(jìn)去的

分類在什么時(shí)機(jī)加載到類里面的

加載流程打印

我們先來看下整個(gè)加載的流程,分類其實(shí)就是在load_categories_nolock里面進(jìn)行加載的,那么我是怎么知道呢,很簡單,全局搜索一下attachCategories看看在哪些地方進(jìn)行調(diào)用了,或者在attachCategories打印下bt

attachCategories 打印bt

通過堆棧信息我們推斷出加載時(shí)機(jī),就如我下面這幅流程圖:

加載時(shí)機(jī)

這種時(shí)機(jī)的前提是主類和分類都實(shí)現(xiàn)了load方法,那么我們假設(shè)一下,假設(shè)LGB分類不實(shí)現(xiàn)lod方法會(huì)是怎么樣的,我們運(yùn)行打印一下

`LGB`不實(shí)現(xiàn)`lod`方法

結(jié)果還是走了LGB這個(gè)分類。 這就意味著,主類和其中一個(gè)分類實(shí)現(xiàn)了load方法,其它分類實(shí)不實(shí)現(xiàn)load方法都是一樣的效果。也就是說只要有一個(gè)分類實(shí)現(xiàn)了load方法,其它分類都會(huì)被標(biāo)記成非懶加載分類。

蘋果這么做就是一次性把所有分類全部處理了,免得每一次還得重新開辟計(jì)算,做一系列操作。

我們再來看看把分類的load全部去掉,就留主類的load方法,然后運(yùn)行

只留主類的`load`

發(fā)現(xiàn)打印的流程少了很多,但還是打印了LGB 里面的kc_instanceMethod1,這是啥情況,我們讀一下MachO,看一下類里面加載了哪些方法

打印類里面的方法

可以看出只要主類有實(shí)現(xiàn)了load方法,那么在編譯的時(shí)候分類的方法也都會(huì)加到類里面去

接下來看一下第三種,主類沒有實(shí)現(xiàn)load方法,分類也沒有實(shí)現(xiàn)load方法

主類和分類都沒有實(shí)現(xiàn)load方法

通過這個(gè)打印的我們發(fā)現(xiàn)realizeClassMaybeSwiftMaybeRelock只有在第一次消息的時(shí)候才會(huì)來到,說明都沒有實(shí)現(xiàn)load方法的時(shí)候推遲到了第一次消息的時(shí)候,我們在readClass里面看一下data有沒有16個(gè)方法,如果有說明就不需要load_images通過緩慢的加載進(jìn)來

readClass里面的方法數(shù)量

再來看最后一種,主類沒有實(shí)現(xiàn)load方法,分類實(shí)現(xiàn)load方法,運(yùn)行打印一下

主類不實(shí)現(xiàn)`load`方法,分類實(shí)現(xiàn)`load`方法

打印完了之后,同樣我們看一下ro里面的方法

ro里面的方法
打印方法

發(fā)現(xiàn)只有8個(gè)了,通過打印可以看出只有本類里面的8個(gè)方法了,那么分類的去哪了,其實(shí)在剛開始的時(shí)候已經(jīng)說過了,在load_categories_nolock里面

load_categories_nolock堆棧

從而驗(yàn)證了這里的流程跟我前面發(fā)的那幅流程圖一摸一樣。

但是由于本類沒有實(shí)現(xiàn)load方法,按理說應(yīng)該是在第一次消息的時(shí)候?qū)崿F(xiàn)去實(shí)現(xiàn)這個(gè)類,但是我們在readClass的時(shí)候已經(jīng)讀到這個(gè)類了,說明有了分類就提前加載了。從而得出一個(gè)結(jié)論:

只要分類實(shí)現(xiàn)了load方法,就要迫使主類提前加載,如果分類和主類都沒實(shí)現(xiàn)load方法,就會(huì)在第一次消息的時(shí)候加載`

所以能不用load方法就不用load方法,不要在應(yīng)用程序啟動(dòng)前加載很多東西。

最后,把整個(gè)類和分類搭配加載的流程用一張PPT來結(jié)束

類和分類搭配加載

iOS 底層原理 文章匯總

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

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