美文网首页
OC底层原理19-类和分类搭配加载

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

作者: Gomu_iOS | 来源:发表于2020-11-18 18:04 被阅读0次

引入

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

类和分类搭配加载

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

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

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

image.png
  • 从打印流程可以看出,类和分类中的方法在Main函数之前就完成了加载,即编译期就完成了加载。

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

image.png
在这里打断点,往下走一步,打印list,验证方法是否从ro取出来了
image.png
  • 发现第一次进入methodizeClass方法,只取了主类GomoPerson里面的方法,然后调用prepareMethodLists,对方法根据sel的地址进行排序,然后存入中,在prepareMethodLists中已实现,可自行查看。

接着又调用了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]));
        }
    }
  • 分类方法也会被prepareMethodLists排序
  • 经过attachLists处理,最后加载的分类,处于list列表第一个位置,这就是为什么分类和类相同方法,会调用分类中的方法多个分类则会调用最后编译的分类中的方法
  • 多个分类相同方法的调用取决于该分类被编译的先后,如下图
    image.png
  • 这里文件顺序的越上面表示越先被编译。
  • 该图这样的顺序则会优先调用GomuPerson+GomuA中的同名方法
  • 如果把GomuPerson+GomuB拖到下面,则会优先调用GomuPerson+GomuB中的同名方法

总结 :

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

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

打印流程


image.png
  • 发现只是进行了类的加载,没有动态添加分类

methodizeClass中下如下断点

image.png
打印list
image.png
  • 发现从data()读取出来的数据中,就已经加载好了类和分类中的所有方法

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

image.png
  • 发现prepareMethodLists方法对list进行了排序,把同名方法按顺序排在前面,其他方法依旧按照sel地址进行排序

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

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

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

打印流程


image.png
  • 发现调用了methodizeClass,这个只有在主类中有load的情况才会调用,但是现在主类中没有实现load依然就调用了,说明非懒加载分类会迫使懒加载类非懒加载类的形式来提前加载数据

methodizeClass中下如下断点

image.png
打印list
image.png
  • 发现这种情况下,在methodizeClass中打印data(),只有主类的方法

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

image.png
  • 第一次还是和第一种情况非懒加载类和非懒加载分类步骤类似,把主类的方法先加入list,自行打印list查看
  • 不同的是,这种情况下,GomuPerson+GomuAGomuPerson+GomuB中的方法被一起加到了list中,如下图打印
    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
  • 类和分类的加载被推迟到了main函数之后,之后调用顺序和第二种情况非懒加载类和懒加载分类一模一样,也是在data()中就完成了类和分类的方法加载

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

二、推荐使用

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

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

  • 属性和方法流程一样,存在本类的property_list_t
  • 协议不会存在申明的类中,而是遵守协议的类中的protocol_list_t
  • 变量在roivars中,属性也会生成_属性名存在ivars

相关文章

网友评论

      本文标题:OC底层原理19-类和分类搭配加载

      本文链接:https://www.haomeiwen.com/subject/wxklmktx.html