OC底层原理19-类和分类搭配加载

2020-11-18  本文已影响0人  Gomu_iOS

引入

OC底层原理18-分类的加载 中,我们探究了分类的加载时机,得出分类和类在是否实现load方法,即是否是懒加载类/分类,类中的方法加载时机不同,这篇我们再来研究一下类和分类中load方法搭配使用的时候,方法的加载时机,属性、协议、变量做一个总结,不做板书,有兴趣可以自己研究。

类和分类搭配加载

搭配情况 分类
情况1 非懒加载类(实现load) 非懒加载分类(实现load)
情况2 非懒加载类(实现load) 懒加载分类(不实现load)
情况3 懒加载类(不实现load) 非懒加载分类(实现load)
情况4 懒加载类(不实现load) 懒加载分类(不实现load)
一、非懒加载类和非懒加载分类

主类实现load方法,分类实现load方法

.main中不对类做任何初始化,依然用分类加载中的工程,运行打印输出流程

image.png

methodizeClass方法中,我们找到了从ro中取出方法,存到

image.png
在这里打断点,往下走一步,打印list,验证方法是否从ro取出来了
image.png

接着又调用了attachCategories添加分类,会先取出中的方法调用attachLists进行处理,然后又会对触发attachCategories分类GomuPerons+GomuB中的方法attachLists进行处理

接着GomuPerons+GomuA再次触发attachCategories进行attachLists处理

attachLists的源码如下

void attachLists(List* const * addedLists, uint32_t addedCount) {
        if (addedCount == 0) return;
        
        if (hasArray()) {
//:-- 第三次添加GomuPerson+GomuA的方法/属性/协议,
//:-- 此时list是一个二维数组,
//:-- 经过此处,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的方法/属性/协议,
//:-- 此时list是一个一维数组
            // 0 lists -> 1 list
            list = addedLists[0];
        } 
        else {
//: -- 第二次添加GomuPerson+GomuB的方法/属性/协议,
//:-- 此时list是一个二维数组,
//:-- 经过此处,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]));
        }
    }

总结 :

  1. 非懒加载类和非懒加载分类情况下,方法的加载会提前到编译期完成,在load_image的时候加载完成。
  2. 如果有类的分类,类中的list会变成一个二维或多维数组。越后编译的分类越插入到list最前面,这也是分类同名方法哪个优先调用的原因。
二、非懒加载类和懒加载分类

主类实现load方法,分类不实现load方法

打印流程


image.png

methodizeClass中下如下断点

image.png
打印list
image.png

过一下断点,等prepareMethodLists执行之后再打印list

image.png

总结:
非懒加载类和懒加载分类的情况下,方法的加载也在编译器完成,不同的是在data()中就已经完成,methodizeClass中只是进行重新排序。

三、懒加载类和非懒加载分类

主类不实现load方法,分类实现load方法

打印流程


image.png

methodizeClass中下如下断点

image.png
打印list
image.png

等经过attachCategories方法后,在attachLists中打下个断点,查看方法何时存到类中

image.png

attachLists最后一个条件中,即1维变2维的方法中,只是进行了memcpy内存拷贝,把GomuPerson移到了list的最后面,然后把addedLists加到了list前面,addedLists[0]中存的是GomuPerson+GomuB,所以调用gomu_instanceMethod1方法,会执行分类GomuBgomu_instanceMethod1的方法

总结:
懒加载类和非懒加载分类情况下,编译期会迫使主类加载,这种情况下也是在编译期就完成了类和分类的加载,但是在load_image之前

四、懒加载类和懒加载分类

主类不实现load方法,分类不实现load方法

打印流程


image.png

main函数中调用一次方法,即objc_msgSend一次,查看打印

image.png

总结:
懒加载类和懒加载分类的情况下,类和分类的加载推迟到了消息第一次调用,在data()中就完成了`方法的加载

二、推荐使用

由上面分析可得出,我们的类中应该减少load方法的使用,不仅可以节约资源,还能减少编译时间,切记切记,慎用load方法

三、属性、协议、变量的加载时机

上一篇下一篇

猜你喜欢

热点阅读