OC文件在编译后,类相关的数据结构会保留在目标文件中,在运行时得到解析和使用。在应用程序运行起来的时候,类的信息会有加载和初始化过程,这个过程就涉及到了类的两个类方法:load
和initialize
。下面我们就来介绍一下这2个方法的区别。
1.load方法
1.1 调用时机
启动程序时,参与编译的类、分类会被加载进内存,load
方法就是在类被加载的时候调用的(前提是这个类有实现load
方法)。
该方法的调用和这个类是否被使用无关,即使一个类在整个程序中都没有用到,也没有任何一个文件去引用该类的头文件,该类的load
方法一样会被调用。
等所有类、分类都加载进内存后才会调用程序的main函数,所以所有类的load
方法都是在main
函数之前被调用的。而且每个类、分类的load
方法都只会被调用一次
1.2 调用顺序
一个城西中如果所有的类、分类都实现了load
方法,那么所有的load
方法都会被调用。他们的执行顺序遵循以下规则:
- 先执行类的
load
方法,再执行所有分类的load
方法 - 执行类的
load
方法时,是按照参与编译的顺序执行,先编译的类先执行。 - 先执行父类的
load
方法,再执行自己的load
方法 - 执行分类的的
load
方法时,按照分类参与编译的顺序,先参与编译的分类先执行
1.3 执行方式
当分类中存在和本类中同名的方法时,调用这个方法最终执行的是分类中的方法。按理说调用load
方法时最终只会调用其中一个分类的load
方法,可是本类和分类都调用了load
方法
因为load
方法和普通方法调用方式不同,普通方法调用时通过消息发送机制实现的,会先去类或元类的方法列表中查找,如果找到了方法就执行,如果没有找到就去父类的方法列表中查找,只要找到就会终止查找,所以只会执行一次。
而load
方法调用时,每个类都是load
方法的地址直接调用,而不会走objc_msgSend
函数的方法查找流程,也就是说一个类有时限load
方法就执行,没有就不执行(没有的话也不会去父类里面查找)
1.4 实现load方法时的注意点
我们通常在load
方法中进行方法交换(Method Swizzle),除此之外,除非真的有必要,我们尽量不要在load
方法中写代码,尤其不要在load
方法中使用其它的类,因为这个时候其它的类可能还没有被加载进内存,随意使用可能会出问题。
如果确实要在load
方法写一些代码,那也要尽量精简代码,不要做一些耗时或者等待锁的操作,因为整个程序在执行load
方法时都会阻塞,从而导致程序启动时间过长甚至无法启动。
2. initialize 方法
2.1 调用时机
initialize
方法是在类或者子类收到第一条消息时被调用,这里的消息就是指实例方法或者类方法的调用,所以所有类的initialize
调用是在main函数后调用的。而且一个类只会调用一次initialize
方法。如果一个类在城西运行过程中一直没有被使用过,那这个类的initialize
方法也就不会被调用
2.2 调用方式
initialize
方法的调用和普通方法调用一样,也是走的objc_msgSend流程。所以如果一个类和他的分类都实现了initialize
方法,那么最终调用的是分类中的方法
如果子类和父类都实现了initialize
方法,那么会先调用父类的方法,然后调用子类的方法。
子类中不需要写[super initialize]来调用父类的方法,通过查看源码得知它是在底层实现过程中主动调用的父类的initialize
方法
2.3 实现initialize方法时的注意点
虽然使用initialize
要比使用load安全(因为在调用initialize
时所有类已经被加载进内存了),但我们还是要尽量少用initialize
这个方法个,尤其要谨慎在分类中实现initialize
方法,因为如果在分类中实现了,本类实现的initialize
方法将不会被调用。实际开发中initialize
方法一般用于初始化全局变量或静态变量。
3 总结
- 调用时机和调用顺序不同:
load方法是在类被加载进内存时调用的,在main函数调用前被调用,且父类,子类,分类都会被调用,父类>子类>分类,同一级别的按编译顺序调用
initialize方法是在第一次方法调用时被调用,在main函数后
- 执行方式不同:
load方法调用时,每个类都是根据load方法的地址直接调用,不会走objc_msgSend流程,也就是说一个类有实现load方法就执行,没有就不执行,没有的话也不会再父类里面查找
initialize方法调用方式和objc_msgSend流程一致,initialize默认会调用
[super initialize]
- 使用模式不同:
load方法常用于方法交换
Method_Swizzle
initialize常用于初始化全局变量或静态变量