Category-initialize
01基本使用
+initialize方法会在类第一次接收到消息时调用
调用顺序
先调用父类的+initialize,再调用子类的+initialize
(先初始化父类,再初始化子类,每个类只会初始化1次)
他只会调用一个,不会调用分类的initialize方法,说明他是通过发消息来调用方法的。
+initialize和+load的很大区别是,+initialize是通过objc_msgSend进行调用的,所以有以下特点
如果子类没有实现+initialize,会调用父类的+initialize(所以父类的+initialize可能会被调用多次),因为他是根据isa来寻找方法的
如果分类实现了+initialize,就覆盖类本身的+initialize调用
在发送initialize的时候,内部会主动发动调用父类的initialize方法。
02源码分析
因为他是类第一次接受消息的时候调用的。因为他最终会转成objc_msgSned(),所以一定是这个方法内部做了一些事情,源码里面是汇编的所以是半开源的,虽然我们无法清晰的看到实现代码,但是他的过程我们是清楚的,
isa->类对象/元类对象,寻找方法,调用
没有的话superclass->类对象/元类对象,寻找方法,调用
因为他在调用方法之前肯定要先调用initialize,所有他可能在第一次寻找方法的时候调用的initialize方法
源码里面有个查找方法的函数
IMP objc_msgLookup(id self, SEL _cmd, ...);
我们可以去这个方法里面看看,他还有一个
class_getInstanceMethod
同过方法追踪我们可以查看到,他的内部会有一个判断,当我们元类中寻找方法的时候,他会看当前的类是否initialize如果没有他就是会执行initialize,在初始化这个类的时候,他会判断父类是否初始化了,如果没有他会先初始化他的父类,初始化完成之后,就会对类进行初始化设置,这样你在下次调用方法的时候就不会在执行initialize了。
03注意点
父类的initialize会被调用多次
因为在第一次查找方法的时候,他会判断有没有父类,并且父类有没有被初始化,如果没有,他会初始化一次,然后再回来初始自己的类,当初始化自己的时候,因为是发消息来寻找的当自己的类对象中没有发现该方法,他就会利用superclass指针到父类的方法列表中寻中,找到了就进行调用,所以,父类的initialize会被调用多次
BOOL sutdentInitialized = NO;
BOOL personInitialized = NO;
BOOL teacherInitialized = NO;
if (!sutdentInitialized) {
if (!personInitialized) {
objc_msgSend([Person class], @selector(initialize));1
personInitialized = YES;
}
objc_msgSend([Student class], @selector(initialize));2
sutdentInitialized = YES;
}
if (!teacherInitialized) {
if (!personInitialized) {
objc_msgSend(Person class], @selector(initialize));
personInitialized = YES;
}
objc_msgSend([Teacher class], @selector(initialize));3
teacherInitialized = YES;
}
总结load 和 initialize
区别
调用方式
load是根据函数地址,
initialize是根据objc_msgSend调用
调用时刻
load是在runtime加载类和分类的时候调用,只会调用一次
initialize是类第一次接受消息的时候调用的,每一个类只会initialize一次,父类的initialize方法可能会被多次调用
调用顺序
load
先调用类的load
先编译的类优先调用load
先调用父类的load再去调用自类的load
再调用分类的load
先编译的分类优先调用
initialize
先初始化父类
再初始化子类(可能最终调用的是父类的)