前言
最近在因为工作的原因,空余时间相对多了一点。所以准备好好整理一下OC相关的基础知识,以便加固相关的知识点。
+load方法
调用时机
-
+load
方法会在runtime加载类和分类时调用; - 每个类和分类在程序运行中只调用一次
+load
方法; - 只要是在Compile Sources中的文件总会被加载,这与这个类是否被用到无关,因此
+load
方法总是在main
函数之前被调用。
调用顺序
- 执行父类的
+load
方法 - 执行当前类的
+load
方法 - 执行分类的
+load
方法 - 不同的类之间方法调用顺序根据Compile Sources中文件的顺序来
下面我们看下实际调用情况:
//Person.m
+ (void)load {
NSLog(@"====== Person load function ======");
}
//Fruit.m
+ (void)load {
NSLog(@"====== Fruit load function ======");
}
//Person子类Student
+(void)load {
NSLog(@"======= Student load function =======");
}
//Person子类 Boy
+(void)load {
NSLog(@"===== Boy load function =====");
}
//Person+Height分类
+(void)load {
NSLog(@"===== Person (Height) load function =====");
}
//Person+Gender分类
+ (void)load {
NSLog(@"======= Person (Gender) load function ========");
}
//运行结果
/*
2019-07-24 17:36:54.231549+0800 LoadDemo[10707:328172] ====== Person load function ======
2019-07-24 17:36:54.232529+0800 LoadDemo[10707:328172] ======= Student load function =======
2019-07-24 17:36:54.232701+0800 LoadDemo[10707:328172] ===== Boy load function =====
2019-07-24 17:36:54.232834+0800 LoadDemo[10707:328172] ====== Fruit load function ======
2019-07-24 17:36:54.232949+0800 LoadDemo[10707:328172] ===== Person (Height) load function =====
2019-07-24 17:36:54.233059+0800 LoadDemo[10707:328172] ======= Person (Gender) load function ========
2019-07-24 17:36:54.233225+0800 LoadDemo[10707:328172] ============== main function =========/n
*/
如果一个类没有实现+load
方法,那么就不会调用它的+load
方法。在Compile Sources中,文件的排放顺讯就是其加载顺序,也就是+load
方法的调用顺序。从其中也可以看出Person
的+load
方法调用顺序优先于子类Boy
和Student
的顺序。同时子类中+load
方法的调用顺序则是Compile Sources
中文件的摆放顺序。
使用场景
从上面可以看出+load
函数调用在main
函数之前,同时+load
方法时的环境很不安全,我们应该尽量减少+load
方法的逻辑。另一个原因是+load
的方法是线程安全的,它内部使用了锁,所以我们应当避免线程阻塞在+load
方法中。
常见的使用+load
方法的场景就是Method Swizzle:
+ (void)load {
Method orginalFunc = class_getInstanceMethod([self class], @selector(originalFunc));
Method swizzleFunc = class_getInstanceMethod([self class], @selector(swizzleFunc));
method_exchangeImplementations(orginalFunc, swizzleFunc);
}
initialize调用
调用时机
initialize方法在第一次给某个类发送消息时调用(比如实例化一个对象),并且只会调用一次。该方法实际上是一种惰性方法,也就是说如果一个类没有被用到,那它的initialize方法就不会调用。
调用顺序
- 先调用父类的
initialize
方法,并且即使子类没有实现initialize
方法,也会调用父类方法。 - 创建子类,如果子类没有实现
initialize
方法,父类initialize
方法会调用多次。因为创建子类对象是要创建父类对象 - 有分类的类会调用分类实现的
initialize
方法,而不调用当前类的initialize
方法。
//Person.m
+ (void)initialize {
NSLog(@"===== Person initialize function =====");
}
//调用
Student *student = [[Student alloc] init];
// Student中未实现initialize方法,打印结果
2019-07-25 15:20:51.585788+0800 LoadDemo[19894:580248] ===== Person initialize function =====
2019-07-25 15:20:51.585972+0800 LoadDemo[19894:580248] ===== Person initialize function =====
//Student中实现initialize方法,打印结果
2019-07-25 15:23:06.068990+0800 LoadDemo[19929:581522] ===== Person initialize function =====
2019-07-25 15:23:06.069200+0800 LoadDemo[19929:581522] ===== Student initialize function =====
要避免分类为实现initialize
方法,导致父类initialize
方法多次调用可以使用如下方式:
+ (void)initialize {
if (self == [Person class]) {
NSLog(@"===== Person initialize function =====");
}
}
使用场景
在initialize中可以初始化一些静态对象
static NSMutableArray *emptyArray;
+ (void)initialize {
if (self == [Person class]) {
emptyArray = [[NSMutableArray alloc] init];
NSLog(@"===== Person initialize function =====");
}
}