面试题引发的思考:
Q: load、initialize方法的区别什么?它们在Category中的调用的顺序?以及出现继承时两者之间的调用过程?

1. Category中initialize方法
// TODO: ----------------- Person类 -----------------
@implementation Person
+ (void)initialize {
NSLog(@"Person + initialize");
}
@end
// TODO: ----------------- Person+Category1类 -----------------
@implementation Person (Category1)
+ (void)initialize {
NSLog(@"Person (Category1) + initialize");
}
@end
// TODO: ----------------- Person+Category2类 -----------------
@implementation Person (Category2)
+ (void)initialize {
NSLog(@"Person (Category2) + initialize");
}
@end
// TODO: ----------------- Student类,继承Person类 -----------------
@implementation Student
+ (void)initialize {
NSLog(@"Student + initialize");
}
@end
// TODO: ----------------- Student (Category1)类 -----------------
@implementation Student (Category1)
+ (void)initialize {
NSLog(@"Student (Category1) + initialize");
}
@end
// TODO: ----------------- Student (Category2)类 -----------------
@implementation Student (Category2)
+ (void)initialize {
NSLog(@"Student (Category2) + initialize");
}
@end
// TODO: ----------------- Teacher类,继承Person类 -----------------
@implementation Teacher
@end
// TODO: ----------------- ViewController类 -----------------
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[Teacher alloc];
// objc_msgSend([Person class], @selector(alloc));
// objc_msgSend([Teacher class], @selector(alloc));
[Student alloc];
// objc_msgSend([Person class], @selector(alloc));
// objc_msgSend([Student class], @selector(alloc));
[Person alloc];
// objc_msgSend([Person class], @selector(alloc));
}
代码如上:
定义一个Person类、两个Person的分类,再定义Person的子类Student、两个Student的分类,然后分别实现initialize方法,最后定义Person的子类Teacher;不做任何操作,直接运行程序则没有打印。
接着放开[Teacher alloc];、[Student alloc];、[Person alloc];语句,运行程序打印结果:
// 打印结果
Demo[1234:567890] Person (Category2) + initialize
Demo[1234:567890] Person (Category2) + initialize
Demo[1234:567890] Student (Category2) + initialize
查看编译顺序:

通过以上代码分析即编译顺序可知:
第一句打印的是
Person (Category2)类的initialize方法,代码执行的是[Teacher alloc];语句。
说明[Teacher alloc];语句会先调用父类Person的initialize方法;第二句打印的是
Person (Category2)类的initialize方法,代码执行的是[Student alloc];语句。
说明[Student alloc];语句会先调用父类Person的initialize方法;第三句打印的是
Student (Category2)类的initialize方法。
说明[Student alloc];语句会先调用父类Person的initialize方法,再调用自己Student的initialize方法;第四句没有打印。
说明[Person alloc];语句调用自己的initialize方法时,前面已经初始化过了;说明每个类只会初始化一次。
2. initialize方法调用原理
通过以上案例分析可得:
initialize方法在类第一次接收消息时调用;- 优先调用父类的
initialize方法,再调用子类的initialize方法;
(先初始化父类,再初始化子类,每个类只初始化一次)
如果分类没有实现initialize方法,则会调用父类的initialize方法
如果分类实现了initialize方法,就会覆盖类本身的initialize方法
3. initialize方法调用顺序
根据OC源码进行分析,首先找到Runtime函数class_getClassMethod:

然后通过class_getInstanceMethod读找到class_getInstanceMethod函数:

然后通过lookUpImpOrNil读找到lookUpImpOrNil函数:

然后通过lookUpImpOrForward读找到lookUpImpOrForward函数:

源码显示:
- 传入的类需要初始化且没有初始化,进行初始化操作,否则跳过;
- 只有在第一次接收消息的时候调用
initialize方法。
然后通过_class_initialize读找到_class_initialize函数:

源码显示:
- 如果传入的类存在父类且父类没有初始化,那么先初始化父类;
- 然后再初始化自己,实现
initialize方法。
4. Category中load方法、initialize方法比较:
Q:
load方法、initialize方法的区别是什么?以及出现继承时两者之间的调用过程?调用方式:
load是根据函数地址直接调用;initialize是通过objc_msgSend调用。调用时刻:
load是Runtime加载类、分类的时候调用(只会调用一次);
initialize是类第一次接收到消息的时候调用,每个类只会调用initialize一次(父类的initialize可能调用多次)。调用顺序:
load
- 先调用类的
load方法;
a> 按照编译的先后顺序调用;
b> 调用子类的load方法之前会先调用父类的load方法;- 再调用分类的
load方法。
a> 按照编译的先后顺序调用
initialize
- 先初始化父类;
- 再初始化子类(可能最终调用的是父类的
initialize方法)。