面试题引发的思考:
Q: Category中load
方法是什么时候调用的?load
方法能继承吗?
-
load
方法在Runtime加载类、分类的时候调用;
即程序启动的时候就调用各个类的load方法; load
方法可以继承,但是一般情况下不会主动去调用load
方法,都是让系统自动调用。
1. Category中load
方法
// TODO: ----------------- Person类 -----------------
@interface Person : NSObject
+ (void)test;
@end
@implementation Person
+ (void)load {
NSLog(@"Person + load");
}
+ (void)test {
NSLog(@"Person + test");
}
@end
// TODO: ----------------- Person+Category1类 -----------------
@implementation Person (Category1)
+ (void)load {
NSLog(@"Person (Category1) + load");
}
+ (void)test {
NSLog(@"Person (Category1) + test");
}
@end
// TODO: ----------------- Person+Category2类 -----------------
@implementation Person (Category2)
+ (void)load {
NSLog(@"Person (Category2) + load");
}
+ (void)test {
NSLog(@"Person (Category2) + test");
}
@end
// TODO: ----------------- ViewController类 -----------------
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// [Person test];
}
// 打印结果
Demo[1234:567890] Person + load
Demo[1234:567890] Person (Category1) + load
Demo[1234:567890] Person (Category2) + load
代码如上:
定义一个Person
类、定义两个Person
的分类,然后分别实现load
方法、test
方法,不做任何操作,直接运行程序得出打印结果。
发现类和分类的load
方法被调用,这是因为:load
方法在Runtime加载类、分类的时候调用。
接着放开[Person test]
语句,调用test
方法,运行程序打印结果:
// 打印结果
Demo[1234:567890] Person + load
Demo[1234:567890] Person (Category1) + load
Demo[1234:567890] Person (Category2) + load
Demo[1234:567890] Person (Category2) + test
查看编译顺序:
分类重写类的方法会优先调用,类的方法存在最后的内存中,所以调用顺序优先按照分类的编译顺序逆序排列。
结论跟打印结果一致。
2. load
方法调用原理
接下来根据OC源码进行分析,首先找到Runtime初始化函数_objc_init
:
然后通过load_images
读取模块找到load_images
函数:
然后通过call_load_methods
读找到call_load_methods
函数:
源码显示:
优先调用类的
load
方法,再调用分类的load
方法。
然后通过call_class_loads
找到call_class_loads
函数:
源码显示:
call_class_loads
函数直接取出类里面的方法,返回方法的地址,通过地址直接调用load
方法。
同理通过call_category_loads
找到call_category_loads
函数:
源码显示:
call_category_loads
函数直接取出分类里面的方法,返回方法的地址,通过地址直接调用load
方法;- 分类中
load
方法的调用顺序是按照编译顺序正序调用的。
Q: test
方法调用方式与load
方法调用方式有何不同?
load
方法调用方式是通过 地址 直接调用;test
方法调用方式是通过 消息发送机制 进行调用。
3. load
方法调用顺序
以上分析可知:
load_images
函数在执行call_load_methods();
语句之前,有一个prepare_load_methods
函数为load
函数处理:
然后通过prepare_load_methods
找到prepare_load_methods
函数:
优先对类进行处理,分类直接按顺序存储。
然后通过schedule_class_load
找到schedule_class_load
函数,看看需要对类做什么样的处理:
首先对
schedule_class_load
函数进行递归操作,先获取父类存储,然后获取子类存储。
通过以上分析可知:
load
方法的调用原理:
load
方法会在Runtime加载类、分类时调用;- 每个类、分类的
load
方法在程序运行过程中只调用一次。
load
方法的调用顺序:
- 先调用类的
load
方法;
a> 按照编译的先后顺序调用;
b> 调用子类的load
方法之前会先调用父类的load
方法;- 再调用分类的
load
方法;
a> 按照编译的先后顺序调用。
接下来通过代码验证一下:
// TODO: ----------------- Person类 -----------------
@implementation Person
+ (void)load {
NSLog(@"Person + load");
}
@end
// TODO: ----------------- Person (Category1)类 -----------------
@implementation Person (Category1)
+ (void)load {
NSLog(@"Person (Category1) + load");
}
@end
// TODO: ----------------- Person (Category2)类 -----------------
@implementation Person (Category2)
+ (void)load {
NSLog(@"Person (Category2) + load");
}
@end
// TODO: ----------------- Student类,继承Person类 -----------------
@implementation Student
+ (void)load {
NSLog(@"Student + load");
}
@end
// TODO: ----------------- Student (Category1)类 -----------------
@implementation Student (Category1)
+ (void)load {
NSLog(@"Student (Category1) + load");
}
@end
// TODO: ----------------- Student (Category2)类 -----------------
@implementation Student (Category2)
+ (void)load {
NSLog(@"Student (Category2) + load");
}
@end
// TODO: ----------------- Animal类,独立的类 -----------------
@implementation Animal
+ (void)load {
NSLog(@"Animal + load");
}
@end
分析以上:
首先调用类的
load
方法,按照编译的先后顺序调用,则先调用的是Student
类,但是调用子类的load
方法之前会先调用父类的load
方法,所以先调用Person
类,再调用Student
类,最后调用Animal
类;再调用分类的
load
方法,按照编译的先后顺序调用,所以调用顺序为:Student (Category1)
、Student (Category2)
、Person (Category1)
、Person (Category2)
。
打印结果如下:
// 打印结果
Demo[1234:567890] Person + load
Demo[1234:567890] Student + load
Demo[1234:567890] Animal + load
Demo[1234:567890] Student (Category1) + load
Demo[1234:567890] Student (Category2) + load
Demo[1234:567890] Person (Category1) + load
Demo[1234:567890] Person (Category2) + load
打印结果与分析结果一致,结论可证。