+load 和 +initialize 理解
+load 底层本质。
OC runtime源码。 objc_init 方法里面 的 load_image 方法回调里面
- 执行 prepare_load_methods , 遍历所有类 调用schedule_class_load , 在这个方法里面 会先 先将父类实现了+load方法的类 添加到loadable_classes struct loadable_class {
Class cls; // may be nil
IMP method;
}类型的数组里面。 然后 再将本类也放到这个数组里面去。 - 在 prepare_load_methods 方法里面 执行完毕 schedule_class_load 在执行 把 所有类别 也都遍历一下 ,找出实现了+load方法的类别 放到 loadable_categories struct loadable_category {
Category cat; // may be nil
IMP method;
}类型的数组里面去。 - 完成以上查找 +load 方法的 类 之后 执行 既 load_images 方法里面 完不成了prepare_load_methods 之后 执行 call_load_methods方法。 开始调用+load方法。
- 在 call_load_methods 里面 会率先执行call_class_loads 在这里会遍历执行 上一步 第2步 添加实现了+load类的 一个数组loadable_classes(里面是loadable_class结构体),然后通过 *load_method(cls, @selector(load)) 方法 直接调用类的+load方法
5.在 call_load_methods 里面,执行完call_class_loads,之后会执行 call_catergroy_loads, 同理会遍历 第2步获得的 loadable_categroies 结构体数组。 然后 同理通过,然后 会多一次保护,判断 该类别当前类 是否存在,并且 完成了+load方法,之后
*load_method(cls, @selector(load)) 方法 直接调用类的+load方法
+initialize 底层本质。
- 通过 +initialize方法苹果概念可知,+initialize方法是在该类第一次被发送消息之前调用。 就是在查找 方法的 时候 会判断 是否是调用过 +initialize 。 如果没有就会调用runtime 消息机制 objc_msgSend(); 调用+initialize 方法。 所以 他遵循 runtime 消息机制。特别的一点是 在调用本类 +initialize方法 之前 会先调用父类的 +initialize方法。然后在调用 本类的 +initialize。
总结。 +load 方法 类与分类 父类与父类分类 实现该方法 才会执行。 不实现 不会执行。 类与分类 父类与父类分类 之间的 +load 方法不会存在覆盖。 只要 实现 就会执行。 并且 类与分类 父类与父类分类 的+load 只会执行一次。 只会被调用一次。调用顺序:父类,类, 分类。 顺序。
类执行完毕+load方法才会执行分类的 +load 的方法。 子类与父类+load 方法的执行顺序与编译顺序无关,父类先于子类执行。(因为+load call_class_loads 里面是先将父类+load 放到数组前边)
然后 父类分类和子类分类 只跟编译顺序有关。先编译 先执行。
+initialize 遵循objc_msgSend()机制。 执行过一次后,就不会再执行。
子类,子类分类和父类 父类分类。 遵循runtime 机制。 如果 分类 实现了 +initialize 就会覆盖 类的+initialize方法。编译顺序最后的分类会覆盖之前的+initializ。
然后源码分析可知。 在执行子类 +initialize 之前 会先调用父类的+initialize方法去执行。所以综上,当一个子类,分类,父类,父类分类 都实现了+initialize方法的时候 实现顺序是
父类分类->子类分类。(分类覆盖了类的+initialize);
特殊。当子类跟子类分类都没有实现+initialize方法的时候,根据runtime机制。会去找父类+initialize方法。所以会出现特殊情况了、就是父类的+initialize方法 会执行两次。但是 在这个方法里面 self 是不同。需要注意。