iOS-浅谈OC中+load和+initialize方法
目录
- +load方法
---- 类和分类的+load方法调用顺序
---- 不同类间的+load方法调用顺序
---- 不同分类间的+load方法调用顺序
---- load总结- +initialize方法
---- +initialize方法调用时机
---- +initialize用途
---- +initialize和+load对比
------------ 调用时机
------------ 调用顺序
------------ 调用方式
+load方法
-
类和分类的+load方法调用顺序
分析源码:
- _objc_init(运行时初始化方法)
↓- load_images(加载模块方法)
↓- call_load_methods(调用load方法)
↓- call_class_loads(调用类的load方法)
↓- call_category_loads(调用分类的load方法)
static void call_class_loads(void)
{
// 读取所有类
struct loadable_class *classes = loadable_classes;
......
// 遍历所有类
for (i = 0; i < used; i++) {
// 找到class对象中的+load方法指针
load_method_t load_method = (load_method_t)classes[i].method;
// 调用+load方法
(*load_method)(cls, @selector(load));
}
......
}
static bool call_category_loads(void)
{
// 读取所有分类
struct loadable_category *cats = loadable_categories;
......
// 遍历所有分类
for (i = 0; i < used; i++) {
// 找到分类里+load方法指针
load_method_t load_method = (load_method_t)cats[i].method;
// 调用+load方法
(*load_method)(cls, @selector(load));
}
......
}
由源码观察可知,先调用了call_class_loads
方法再调用了call_category_loads
方法。
所以类和分类调用顺序是:
先调用所有类中的+load方法,再调用所有分类的+load方法。
-
不同类间的+load方法调用顺序
分析运行时源码:
- prepare_load_methods(预加载+load方法)
↓- schedule_class_load(定制类的load方法)
static void schedule_class_load(Class cls)
{
if (!cls) return;
ASSERT(cls->isRealized()); // _read_images should realize
if (cls->data()->flags & RW_LOADED) return;
// Ensure superclass-first ordering
schedule_class_load(cls->getSuperclass());
add_class_to_loadable_list(cls);
cls->setInfo(RW_LOADED);
}
通过分析源码可知:schedule_class_load方法内部有一个递归调用,每次传入参数是cls的superclass,将cls添加到loadable_classes数组中,所以最终结果导致在loadable_classes数组中父类class会排在子类class前面,再结合call_class_loads方法得出:
- 1、先调用父类中的+load方法,再调用子类中的+load方法。
- 2、没有继承关系的类,按照编译顺序调用+load方法。
-
不同分类间的+load方法调用顺序
void add_category_to_loadable_list(Category cat)
{
......
loadable_categories[loadable_categories_used].cat = cat;
......
}
结合call_category_loads方法得出:
分类是按照编译顺序调用+load方法。
-
load总结
- +load方法会在runtime加载类、分类时调用。
- 每个类、分类的+load,在程序运行过程中只调用一次。
- +load方法是根据方法地址直接调用,并不是经过objc_msgSend函数调用
调用顺序
- 先调用类的+load
- 按照编译先后顺序调用(先编译,先调用)
- 调用子类的+load之前会先调用父类的+load
- 再调用分类的+load
- 按照编译先后顺序调用(先编译,先调用)
+initialize方法
-
+initialize方法调用时机
查看
objc_msgSend
源码,因为是汇编语言理解起来困难,所以可以从消息发送的过程入手。
isa->找到类/元类对象->查找方法->调用
从查找方法部分入手
- class_getInstanceMethod(查找对象方法)/class_getClassMethod(查找类方法)
↓- lookUpImpOrForward(查找方法实现)
IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior)
{
......
// 如果没有初始化,需要调用initialize方法
if ((behavior & LOOKUP_INITIALIZE) && !cls->isInitialized()) {
initializeNonMetaClass (_class_getNonMetaClass(cls, inst));
}
......
}
initializeNonMetaClass:
void initializeNonMetaClass(Class cls)
{
......
// 拿到父类
supercls = cls->getSuperclass();
// 父类如果没有初始化,先初始化父类
if (supercls && !supercls->isInitialized()) {
initializeNonMetaClass(supercls);
}
......
callInitialize(cls);
......
}
// 调用cls的initialize方法
void callInitialize(Class cls)
{
((void(*)(Class, SEL))objc_msgSend)(cls, @selector(initialize));
asm("");
}
通过查看源码可知,在
objc_msgSend
(消息发送)过程中,会先调用类的+initialize方法。
观察到initializeNonMetaClass
方法内部有递归调用,递归参数是superclass
,所以调用子类+initialize前,会先判断父类是否已经初始化了,如果没有会先调用父类的+initialize
方法。
总结:
- 1、+initialize方法会在类第一次接收到消息时调用。
- 2、调用顺序
先调用父类的+initialize,再调用子类的+initialize(先初始化父类,再初始化子类,每个类只会初始化1次)
-
+initialize用途
initialize用途就是在第一次使用这个类的时候做一些事情。
-
+initialize和+load对比
-
调用时机
+load在runtime加载类、分类时调用(只会调用一次)
+initialize是类第一次接收到消息时调用,每一个类只会initialize一次(如果子类没有实现initialize方法,父类的initialize方法可能会被调用多次)
-
调用顺序
+load
- 1、先调用类的+load
先编译的类,优先调用+load
调用子类+load之前,会先调用父类的+load- 2、再调用分类
先编译的分类,优先调用+load
+initialize
- 1、先初始化父类
- 2、再初始化子类(可能最终调用的是父类的+initialize方法)
-
调用方式
+load是通过方法指针调用,+initialize是通过objc_msgSend方式调用,所以+initialize会有以下特点:
- 1、如果子类没有实现+initialize方法,会调用父类的+initialize(所以父类的+initialize可能会被调用多次,第一次为自己初始化时调用,其他为子类消息发送调用)
- 2、如果分类实现了+initialize,会覆盖主类的+initialize。