一、load
Apple文档中这样描述,传送门
1、load源码解析
调用方式
类load调用源码
load的方法中直接拿到load方法的内存地址直接调用方法。
分类调用源码
分类中也是通过直接拿到load方法的地址进行调用。因此正如我们之前试验的一样,分类中重写load方法,并不会优先调用分类的load方法,而不调用本类中的load方法了。
2、 load函数调用特点
当类被引用进项目的时候就会执行load函数(在main函数开始执行之前),与这个类是否被用到无关,每个类的load函数只会自动调用一次;由于load函数是系统自动加载的,因此不需要调用父类的load函数,否则父类的load函数会多次执行。
- ①.当父类和子类都实现load函数时,父类的load方法执行顺序要优先于子类;
- ②.当子类未实现load方法时,不会调用父类的load方法;(注:此处跟父类load执行无关,意思是子类没有实现load,父类的load不会执行两次)
- ③.类中的load方法执行顺序要优先于类别(Category);
- ④.当有多个类别(Category)都实现了load方法,这几个load方法都会执行,但执行顺序不确定(其执行顺序和类别在Compile Sources中出现的顺序一致)
- ⑤.当有多个不同的类的时候,每个类load 执行顺序与其在Compile Sources出现的顺序一致。
3、验证
新建两个类:Person和Child,Person继承NSObject,Child继承Person
Person : NSObject
Child : Person
新建两个分类:Person (Test)和Child (Test)
Person (Test)
Child (Test)
load的运行结果
load——Person
load——Child
load——Child (Test)
load——Person (Test)
二、initialize
Apple文档中这样描述,传送门
1、initialize源码解析
我们知道当类第一次接收到消息时,就会调用initialize,相当于第一次使用类的时候就会调用initialize方法。调用子类的initialize之前,会先保证调用父类的initialize方法,如果之前已经调用过initialize,就不会再调用initialize方法了。当分类重写initialize方法时会先调用分类的方法。
上图中我们发现,initialize是通过消息发送机制调用的,消息发送机制通过isa指针找到对应的方法与实现,因此先找到分类方法中的实现,会优先调用分类方法中的实现。
2、 initialize函数调用特点
initialize在类或者子类的第一个方法被调用前调用。即使类文件被引用进项目,但是没有使用,initialize也不会被调用。由于是系统自动调用,也不需要再调用[super initialize],否则父类的initialize会被多次执行。假如这个类放到代码中,而这段代码并没有执行,这个函数是不会执行的。
- ①.父类的initialize方法会比子类先执行;
- ②.当子类未实现initialize方法时,会调用父类initialize方法,子类实现initialize方法时,会覆盖父类initialize方法;
- ③.当有多个Category都实现了initialize方法,会覆盖类种的方法,只执行一个(会执行Compile Sources 列表中最后一个Category 的initialize方法)
三、load和initialize使用场景
load
由于调用load方法时的环境很不安全,我们应该尽量减少load方法的逻辑。另一个原因是load方法是线程安全的,它内部使用了锁,所以我们应该避免线程阻塞在load方法中
load很常见的一个使用场景,交换两个方法的实现
//摘自MJRefresh
+ (void)load
{
[self exchangeInstanceMethod1:@selector(reloadData) method2:@selector(mj_reloadData)];
[self exchangeInstanceMethod1:@selector(reloadRowsAtIndexPaths:withRowAnimation:) method2:@selector(mj_reloadRowsAtIndexPaths:withRowAnimation:)];
[self exchangeInstanceMethod1:@selector(deleteRowsAtIndexPaths:withRowAnimation:) method2:@selector(mj_deleteRowsAtIndexPaths:withRowAnimation:)];
[self exchangeInstanceMethod1:@selector(insertRowsAtIndexPaths:withRowAnimation:) method2:@selector(mj_insertRowsAtIndexPaths:withRowAnimation:)];
[self exchangeInstanceMethod1:@selector(reloadSections:withRowAnimation:) method2:@selector(mj_reloadSections:withRowAnimation:)];
[self exchangeInstanceMethod1:@selector(deleteSections:withRowAnimation:) method2:@selector(mj_deleteSections:withRowAnimation:)];
[self exchangeInstanceMethod1:@selector(insertSections:withRowAnimation:) method2:@selector(mj_insertSections:withRowAnimation:)];
}
+ (void)exchangeInstanceMethod1:(SEL)method1 method2:(SEL)method2
{
method_exchangeImplementations(class_getInstanceMethod(self, method1), class_getInstanceMethod(self, method2));
}
initialize
initialize方法主要用来对一些不方便在编译期初始化的对象进行赋值。
比如NSMutableArray这种类型的实例化依赖于runtime的消息发送,所以显然无法在编译器初始化:
// In Person.m
// int类型可以在编译期赋值
static int someNumber = 0;
static NSMutableArray *someArray;
+ (void)initialize {
if (self == [Person class]) {
// 不方便编译期复制的对象在这里赋值
someArray = [[NSMutableArray alloc] init];
}
}
四、总结
-
load和initialize方法都会在实例化对象之前调用
-
load执行在main函数以前,initialize执行main函数之后
-
这两个方法会被自动调用,不能手动调用它们。
-
load和initialize方法都不用显示的调用父类的方法而是自动调用
-
子类没有initialize方法也会调用父类的方法,而load方法则不会调用父类
-
initialize方法对一个类而言只会调用一次(Person、或者是Person+Category)都是一个Perosn类。load方法则是每个都会调用,只要你写了load方法,添加到工程都会实现。
-
load方法通常用来进行Method Swizzle,initialize方法一般用于初始化全局变量或静态变量。
-
load和initialize方法内部使用了锁,因此它们是线程安全的。实现时要尽可能保持简单,避免阻塞线程,不要再使用锁。