OC底層原理19-類和分類搭配加載

引入

OC底層原理18-分類的加載 中,我們探究了分類的加載時機(jī),得出分類和類在是否實(shí)現(xiàn)load方法,即是否是懶加載類/分類,類中的方法加載時機(jī)不同,這篇我們再來研究一下類和分類中load方法搭配使用的時候,方法的加載時機(jī),屬性、協(xié)議、變量做一個總結(jié),不做板書,有興趣可以自己研究。

類和分類搭配加載

搭配情況 分類
情況1 非懶加載類(實(shí)現(xiàn)load) 非懶加載分類(實(shí)現(xiàn)load)
情況2 非懶加載類(實(shí)現(xiàn)load) 懶加載分類(不實(shí)現(xiàn)load)
情況3 懶加載類(不實(shí)現(xiàn)load) 非懶加載分類(實(shí)現(xiàn)load)
情況4 懶加載類(不實(shí)現(xiàn)load) 懶加載分類(不實(shí)現(xiàn)load)
一、非懶加載類和非懶加載分類

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

.main中不對類做任何初始化,依然用分類加載中的工程,運(yùn)行打印輸出流程

image.png

  • 從打印流程可以看出,類和分類中的方法在Main函數(shù)之前就完成了加載,即編譯期就完成了加載。

methodizeClass方法中,我們找到了從ro中取出方法,存到

image.png

在這里打斷點(diǎn),往下走一步,打印list,驗(yàn)證方法是否從ro取出來了
image.png

  • 發(fā)現(xiàn)第一次進(jìn)入methodizeClass方法,只取了主類GomoPerson里面的方法,然后調(diào)用prepareMethodLists,對方法根據(jù)sel的地址進(jìn)行排序,然后存入中,在prepareMethodLists中已實(shí)現(xiàn),可自行查看。

接著又調(diào)用了attachCategories添加分類,會先取出中的方法調(diào)用attachLists進(jìn)行處理,然后又會對觸發(fā)attachCategories分類GomuPerons+GomuB中的方法attachLists進(jìn)行處理

接著GomuPerons+GomuA再次觸發(fā)attachCategories進(jìn)行attachLists處理

attachLists的源碼如下

void attachLists(List* const * addedLists, uint32_t addedCount) {
        if (addedCount == 0) return;
        
        if (hasArray()) {
//:-- 第三次添加GomuPerson+GomuA的方法/屬性/協(xié)議,
//:-- 此時list是一個二維數(shù)組,
//:-- 經(jīng)過此處,GomuPerson+GomuA的方法列表被加到了list[0],
//:-- GomuPerson+GomuB的方法列表被移到了list[1],
//:-- GomuPerson的方法列表被移到了list[2]
            // many lists -> many lists
            uint32_t oldCount = array()->count;
            uint32_t newCount = oldCount + addedCount;
            setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));
            array()->count = newCount;
            memmove(array()->lists + addedCount, array()->lists, 
                    oldCount * sizeof(array()->lists[0]));
            memcpy(array()->lists, addedLists, 
                   addedCount * sizeof(array()->lists[0]));
        }
        else if (!list  &&  addedCount == 1) {
//:-- 第一次添加GomuPerson的方法/屬性/協(xié)議,
//:-- 此時list是一個一維數(shù)組
            // 0 lists -> 1 list
            list = addedLists[0];
        } 
        else {
//: -- 第二次添加GomuPerson+GomuB的方法/屬性/協(xié)議,
//:-- 此時list是一個二維數(shù)組,
//:-- 經(jīng)過此處,GomuPerson+GomuB的方法被加到了list[0],
//:-- GomuPerson的方法被移到了list[1]
            // 1 list -> many lists
            List* oldList = list;
            uint32_t oldCount = oldList ? 1 : 0;
            uint32_t newCount = oldCount + addedCount;
            setArray((array_t *)malloc(array_t::byteSize(newCount)));
            array()->count = newCount;
            if (oldList) array()->lists[addedCount] = oldList;
            memcpy(array()->lists, addedLists, 
                   addedCount * sizeof(array()->lists[0]));
        }
    }
  • 分類方法也會被prepareMethodLists排序
  • 經(jīng)過attachLists處理,最后加載的分類,處于list列表第一個位置,這就是為什么分類和類相同方法,會調(diào)用分類中的方法,多個分類則會調(diào)用最后編譯的分類中的方法
  • 多個分類相同方法的調(diào)用取決于該分類被編譯的先后,如下圖
    image.png
  • 這里文件順序的越上面表示越先被編譯。
  • 該圖這樣的順序則會優(yōu)先調(diào)用GomuPerson+GomuA中的同名方法
  • 如果把GomuPerson+GomuB拖到下面,則會優(yōu)先調(diào)用GomuPerson+GomuB中的同名方法

總結(jié) :

  1. 非懶加載類和非懶加載分類情況下,方法的加載會提前到編譯期完成,在load_image的時候加載完成。
  2. 如果有類的分類,類中的list會變成一個二維或多維數(shù)組。越后編譯的分類越插入到list最前面,這也是分類同名方法哪個優(yōu)先調(diào)用的原因。
二、非懶加載類和懶加載分類

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

打印流程


image.png
  • 發(fā)現(xiàn)只是進(jìn)行了類的加載,沒有動態(tài)添加分類

methodizeClass中下如下斷點(diǎn)

image.png

打印list
image.png

  • 發(fā)現(xiàn)從data()讀取出來的數(shù)據(jù)中,就已經(jīng)加載好了類和分類中的所有方法

過一下斷點(diǎn),等prepareMethodLists執(zhí)行之后再打印list

image.png

  • 發(fā)現(xiàn)prepareMethodLists方法對list進(jìn)行了排序,把同名方法按順序排在前面,其他方法依舊按照sel地址進(jìn)行排序

總結(jié):
非懶加載類和懶加載分類的情況下,方法的加載也在編譯器完成,不同的是在data()中就已經(jīng)完成,methodizeClass中只是進(jìn)行重新排序。

三、懶加載類和非懶加載分類

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

打印流程


image.png
  • 發(fā)現(xiàn)調(diào)用了methodizeClass,這個只有在主類中有load的情況才會調(diào)用,但是現(xiàn)在主類中沒有實(shí)現(xiàn)load依然就調(diào)用了,說明非懶加載分類會迫使懶加載類非懶加載類的形式來提前加載數(shù)據(jù)

methodizeClass中下如下斷點(diǎn)

image.png

打印list
image.png

  • 發(fā)現(xiàn)這種情況下,在methodizeClass中打印data(),只有主類的方法

等經(jīng)過attachCategories方法后,在attachLists中打下個斷點(diǎn),查看方法何時存到類中

image.png

  • 第一次還是和第一種情況非懶加載類和非懶加載分類步驟類似,把主類的方法先加入list,自行打印list查看
  • 不同的是,這種情況下,GomuPerson+GomuAGomuPerson+GomuB中的方法被一起加到了list中,如下圖打印
    image.png

attachLists最后一個條件中,即1維變2維的方法中,只是進(jìn)行了memcpy內(nèi)存拷貝,把GomuPerson移到了list的最后面,然后把addedLists加到了list前面,addedLists[0]中存的是GomuPerson+GomuB,所以調(diào)用gomu_instanceMethod1方法,會執(zhí)行分類GomuBgomu_instanceMethod1的方法

總結(jié):
懶加載類和非懶加載分類情況下,編譯期會迫使主類加載,這種情況下也是在編譯期就完成了類和分類的加載,但是在load_image之前

四、懶加載類和懶加載分類

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

打印流程


image.png
  • 懶加載類和懶加載分類的情況下,類和分類都不會提前在編譯期加載。

main函數(shù)中調(diào)用一次方法,即objc_msgSend一次,查看打印

image.png

  • 類和分類的加載被推遲到了main函數(shù)之后,之后調(diào)用順序和第二種情況非懶加載類和懶加載分類一模一樣,也是在data()中就完成了類和分類的方法加載

總結(jié):
懶加載類和懶加載分類的情況下,類和分類的加載推遲到了消息第一次調(diào)用,在data()中就完成了`方法的加載

二、推薦使用

由上面分析可得出,我們的類中應(yīng)該減少load方法的使用,不僅可以節(jié)約資源,還能減少編譯時間,切記切記,慎用load方法

三、屬性、協(xié)議、變量的加載時機(jī)

  • 屬性和方法流程一樣,存在本類的property_list_t
  • 協(xié)議不會存在申明的類中,而是遵守協(xié)議的類中的protocol_list_t
  • 變量在roivars中,屬性也會生成_屬性名存在ivars
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

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