引入
在 OC底层原理18-分类的加载 中,我们探究了分类的加载时机,得出分类和类在是否实现load
方法,即是否是懒加载类/分类,类中的方法加载时机不同,这篇我们再来研究一下类和分类中load
方法搭配使用的时候,方法的加载时机,属性、协议、变量做一个总结,不做板书,有兴趣可以自己研究。
类和分类搭配加载
搭配情况 | 类 | 分类 |
---|---|---|
情况1 | 非懒加载类(实现load) | 非懒加载分类(实现load) |
情况2 | 非懒加载类(实现load) | 懒加载分类(不实现load) |
情况3 | 懒加载类(不实现load) | 非懒加载分类(实现load) |
情况4 | 懒加载类(不实现load) | 懒加载分类(不实现load) |
一、非懒加载类和非懒加载分类
主类实现load
方法,分类实现load
方法
.main
中不对类做任何初始化,依然用分类加载中的工程,运行打印输出流程
- 从打印流程可以看出,
类和分类
中的方法在Main函数
之前就完成了加载,即编译期就完成了加载。
在methodizeClass
方法中,我们找到了从ro
中取出方法
,存到类
中
在这里打断点,往下走一步,打印
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
中的同名方法
总结 :
- 非懒加载类和非懒加载分类情况下,方法的加载会提前到编译期完成,在load_image的时候加载完成。
- 如果有类的分类,类中的list会变成一个二维或多维数组。越后编译的分类越插入到
list
最前面,这也是分类同名方法哪个优先调用的原因。
二、非懒加载类和懒加载分类
主类实现load方法,分类不实现load方法
打印流程
image.png
- 发现只是进行了
类的加载
,没有动态添加分类
在methodizeClass
中下如下断点
打印
list
image.png
- 发现从
data()
读取出来的数据中,就已经加载好了类和分类
中的所有方法
过一下断点,等prepareMethodLists
执行之后再打印list
- 发现
prepareMethodLists
方法对list
进行了排序,把同名方法按顺序排在前面,其他方法依旧按照sel
地址进行排序
总结:
非懒加载类和懒加载分类的情况下,方法的加载也在编译器完成,不同的是在data()
中就已经完成,methodizeClass
中只是进行重新排序。
三、懒加载类和非懒加载分类
主类不实现load方法,分类实现load方法
打印流程
image.png
- 发现调用了
methodizeClass
,这个只有在主类
中有load
的情况才会调用,但是现在主类中没有实现load
依然就调用了,说明非懒加载分类
会迫使懒加载类
以非懒加载类
的形式来提前加载数据
在methodizeClass
中下如下断点
打印
list
image.png
- 发现这种情况下,在
methodizeClass
中打印data()
,只有主类的方法
等经过attachCategories
方法后,在attachLists
中打下个断点,查看方法何时存到类中
- 第一次还是和第一种情况
非懒加载类和非懒加载分类
步骤类似,把主类
的方法先加入list
,自行打印list
查看 - 不同的是,这种情况下,
GomuPerson+GomuA
和GomuPerson+GomuB
中的方法被一起加到了list
中,如下图打印
image.png
在attachLists
最后一个条件中,即1维变2维的方法中,只是进行了memcpy
内存拷贝,把GomuPerson
移到了list
的最后面,然后把addedLists
加到了list
前面,addedLists[0]
中存的是GomuPerson+GomuB
,所以调用gomu_instanceMethod1
方法,会执行分类GomuB
中gomu_instanceMethod1
的方法
总结:
懒加载类和非懒加载分类情况下,编译期会迫使主类加载,这种情况下也是在编译期就完成了类和分类的加载,但是在load_image
之前
四、懒加载类和懒加载分类
主类不实现load方法,分类不实现load方法
打印流程
image.png
-
懒加载类和懒加载分类
的情况下,类和分类
都不会提前在编译期加载。
在main
函数中调用一次方法,即objc_msgSend
一次,查看打印
-
类和分类
的加载被推迟到了main
函数之后,之后调用顺序和第二种情况非懒加载类和懒加载分类
一模一样,也是在data()
中就完成了类和分类
的方法加载
总结:
懒加载类和懒加载分类的情况下,类和分类
的加载推迟到了消息第一次调用
,在data()
中就完成了`方法的加载
二、推荐使用
由上面分析可得出,我们的类中应该减少load方法的使用,不仅可以节约资源,还能减少编译时间,切记切记,慎用load方法
三、属性、协议、变量的加载时机
- 属性和方法流程一样,存在本类的
property_list_t
中 - 协议不会存在申明的类中,而是遵守协议的类中的
protocol_list_t
中 - 变量在
ro
的ivars
中,属性也会生成_属性名存在ivars
中
网友评论