load_images 流程分析
load_images 主要分为两个流程:
- prepare_load_methods:准备 load 方法
- call_load_methods:调用 load 方法
流程一:prepare_load_methods
处理非懒加载类
-
获取所有的非懒加载类(实现了load方法的类)
classref_t const *classlist = _getObjc2NonlazyClassList(mhdr, &count);
-
计划添加(load)类
这个方法中有一个递归,递归父类
保证父类的load方法在子类的load方法之前执行
static void schedule_class_load(Class cls) { // Ensure superclass-first ordering schedule_class_load(cls->superclass); add_class_to_loadable_list(cls); }
-
loadable_classes
表分析:这是一个全局数组
- 初始化以及扩容
if (loadable_classes_used == loadable_classes_allocated) { loadable_classes_allocated = loadable_classes_allocated*2 + 16; loadable_classes = (struct loadable_class *) realloc(loadable_classes, loadable_classes_allocated * sizeof(struct loadable_class)); }
- 添加实现load方法的类的操作
loadable_classes[loadable_classes_used].cls = cls; loadable_classes[loadable_classes_used].method = method; loadable_classes_used++;
- 全局变量的定义
static struct loadable_class *loadable_classes = nil; static int loadable_classes_used = 0; static int loadable_classes_allocated = 0;
- loadable_class:实现load方法的类的数组 - loadable_classes_used:当前记录的类的个数 - loadable_classes_allocated:当前创建的类的个数
-
loadable_class
的结构:cls
+method
struct loadable_class { Class cls; // may be nil IMP method; };
处理非懒加载类
相同之处
- 获取所有的非懒加载分类:
category_t * const *categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);
- 添加分类到
loadable_list
中:add_category_to_loadable_list(cat);
,这个方法的实现流程和逻辑与add_class_to_loadable_list(cls);
方法一模一样。
不同之处
添加分类前会调用一次类的初始化方法:
realizeClassWithoutSwift(cls, nil);
分类的信息是保存在
loadable_categories
变量中;-
分类记录的信息是:分类(cate)+方法(method)
struct loadable_category { Category cat; // may be nil IMP method; };
流程二:call_load_methods
完整的分析了上面的流程,再来看这个流程就简直不要太简单。为什么这么说呢?
源码很短,直接上(只保留主要代码):
void call_load_methods(void)
{
void *pool = objc_autoreleasePoolPush();
do {
// 1. Repeatedly call class +loads until there aren't any more
while (loadable_classes_used > 0) {
call_class_loads();
}
// 2. Call category +loads ONCE
more_categories = call_category_loads();
// 3. Run more +loads if there are classes OR more untried categories
} while (loadable_classes_used > 0 || more_categories);
objc_autoreleasePoolPop(pool);
}
苹果给出的注释已经非常详细了
- 调用类的load方法:
call_class_loads();
,就是这个loadable_classes
数组中的类的load方法; - 调用分类的load方法:
call_category_loads();
,就是loadable_categories
数组中的分类的load方法; - 遍历下去,直到全部调用
这里解释了2个问题,看懂这个流程,简直太简单了,我都不想解释了。
1. 主类的load方法和分类的load方法是否会同时调用还是只调用某一个
会同时调用
2. 为什么主类的load方法在分类的load方法前面调用
因为在 call_load_methods 方法中,先调用的是 call_class_loads();
主类的load方法,然后再调用 call_category_loads();
分类的laod方法
分类的初探
分类的结构:通过cpp文件分析
struct _category_t {
const char *name;
struct _class_t *cls;
const struct _method_list_t *instance_methods;
const struct _method_list_t *class_methods;
const struct _protocol_list_t *protocols;
const struct _prop_list_t *properties;
};
分类的结构:通过objc源码分析
在 objc 源码里面也有很清晰的判断 是否是元类
-
instance_methods
:attachList 到类里面 -
class_methods
: attachList 到元类里面
为什么分类的方法会“覆盖”主类的方法
- 分类是在类的初始化完成之后才加载的;
- 分类的方法是通过attach到类的方法列表中;
- 根据attachLists的流程,后添加的方法被插在了列表前面;
- 在寻找方法的时候,会按照列表的顺序查找,如果找到了指定方法就不会再往后面寻找,所以造成了“覆盖”的假象,实际上两个方法都在方法列表中。
懒加载类与非懒加载类
懒加载类
- 没有实现load方法
- 在运行时调用这个类的时候才会加载
非懒加载类
- 实现了load方法
- 如果有子类实现了load方法,则这个类也在编译阶段加载
- 如果在其他类的load方法中被调用,则这个类也在编译阶段加载
- 将类的加载提前到了编译阶段
怎么理解将类的加载提前到了编译阶段?
我们需要先知道编译阶段做了啥。
在平时开发中,cmd+b
,也就是我们常说的 编译一下
,但真的只是 编译一下
吗?build
是构建的意思,既然是构建,肯定要有构建的东西出来。确实是的,构建的结果就是藏在我们xcode的项目目录的 product
文件夹下面的那个文件,俗称 APP
,但实际上是一个可执行文件
。所以,编译可以理解为从代码到可执行文件的过程
这个过程经历了啥呢。前面将 [_dyld] 的时候讲过了,流程是:
源文件(.m/.h)
-> 预处理(.cpp)
-> 预处理完成(.i)
-> Compile编译(gcc)
-> Assembly(.s)
-> Assembly(as)
-> 镜像文件(.o)
-> _dyld链接
-> 可执行文件
类与分类的搭配加载
1. 懒加载的分类(没有实现load)
1. 懒加载类(没有实现load)
_objc_init
_read_images
-
_getObjc2CategoryList
:取得所有懒加载分类GETSECT(_getObjc2CategoryList, category_t *, "__objc_catlist"); GETSECT(_getObjc2NonlazyCategoryList, category_t *, "__objc_nlcatlist");
-
addUnattachedCategoryForClass
:保存分类的方法到表中category_map = NXCreateMapTable(NXPtrValueMapPrototype, 16); NXMapTable *cats = unattachedCategories(); NXMapInsert(cats, cls, list);
load_images
prepare_load_methods
realizeClassWithoutSwift
methodizeClass
attachCategories
2. 非懒加载类(实现了load)
_objc_init
_read_images
_getObjc2CategoryList
addUnattachedCategoryForClass
remethodizeClass
tachCategories
2. 非懒加载分类(实现了load)
1. 懒加载类(没有实现load)
alloc
_objc_msgSend_uncached
lookUpImpOrForward
initializeAndLeaveLocked
-
realizeClassWithoutSwift
:在这里将所有方法存在了ro中 methodizeClass
2. 非懒加载类(实现了load)
_objc_init
_read_images
realizeClassWithoutSwift
methodizeClass