面试题引发的思考:
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
方法)。