前言
今天的在项目里面做版本兼容的时候,需要利用 runtime 做 Method Swizzling,一开始把 Method Swizzling 代码写在了 + (void)initialize中,后来发现了这种做法是不对的。感觉很有必要深入了解关于 NSObject的load和initialize方法。所以才有了这一篇博客。
load 与 initialize
OC 是一门面向对象的动态语言,它的类被加载和初始化的时候,可以收到方法回调,可以在适当的情况下做一些定制处理。而这正是load和initialize方法可以帮我们做到的。在我们理解这两个方法之前,先来看看官方的文档是怎么说的吧~
load
initialize
我从官方文档中总结如下:
load
在iOS上通常就是App启动时,load 方法是只要类所在文件被引用就会被调用 一个类的 load方法不用写明[super load],父类就会收到调用。且 父类(Superclass)的方法优先于子类(Subclass)的方法,类中的方法优先于类别(Category)中的方法。 对于一个类而言,没有 load 方法实现就不会调用,不会考虑对 NSObject 的继承。
initialize
initialize 是在类或者其子类的第一个方法被调用前调用。initialize 的运行过程中是能保证线程安全的。和 load 不同,即使子类不实现 initialize 方法,会把父类的实现继承过来调用一遍。注意的是在此之前,父类的方法已经被执行过一次了,同样不需要 super 调用。
代码示例验证
代码1:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
| #import "ViewController.h"
@interface SuperClass : NSObject
@end
@interface ChildClass : SuperClass
@end
@interface SuperClass (SuperClassCategory)
@end
@interface ChildClass (ChildClassCategory)
@end
@implementation SuperClass
+(void)load { NSLog(@"%s", __FUNCTION__); }
+(void)initialize { NSLog(@"%s", __FUNCTION__); }
@end
@implementation ChildClass
+(void)load { NSLog(@"%s", __FUNCTION__); }
+(void)initialize { NSLog(@"%s", __FUNCTION__); }
@end
@implementation SuperClass (SuperClassCategory)
+(void)load { NSLog(@"%s", __FUNCTION__); }
+(void)initialize { NSLog(@"%s", __FUNCTION__); }
@end
@implementation ChildClass (ChildClassCategory)
+(void)load { NSLog(@"%s", __FUNCTION__); }
+(void)initialize { NSLog(@"%s", __FUNCTION__); }
@end
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad { [super viewDidLoad]; }
- (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; }
@end
|
运行结果:
在上面的代码方法 - (void)viewDidLoad
里添加:
1 2
| [[SuperClass alloc] init]; [[ChildClass alloc] init];
|
运行结果:
这个结果最基本的验证了官方文档所说的内容。
代码2:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
| #import "ViewController.h"
@interface AClass : NSObject
@end
@interface AClass (AClassCategory1)
@end
@interface AClass (AClassCategory2)
@end
@implementation AClass
+(void)load { NSLog(@"%s", __FUNCTION__); }
+(void)initialize { NSLog(@"%s", __FUNCTION__); }
@end
@implementation AClass (AClassCategory1)
+(void)load { NSLog(@"%s", __FUNCTION__); }
+(void)initialize { NSLog(@"%s", __FUNCTION__); }
@end
@implementation AClass (AClassCategory2)
+(void)load { NSLog(@"%s", __FUNCTION__); }
+(void)initialize { NSLog(@"%s", __FUNCTION__); }
@end
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad { [super viewDidLoad]; [[AClass alloc] init]; }
- (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; }
@end
|
运行结果:
只有最后一个类别(Category)的+(void)initialize执行,其他两个都被隐藏。而对于+(void)load,三个都执行,并且如果Apple的文档中介绍顺序一样:先执行类自身的实现,再执行类别(Category)中的实现。
代码3:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| #import "ViewController.h"
@interface SuperClass : NSObject
@end
@interface ChildClass : SuperClass
@end
@implementation SuperClass
+(void)load { NSLog(@"%s", __FUNCTION__); }
+(void)initialize { NSLog(@"%s", __FUNCTION__); }
@end
@implementation ChildClass
@end
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad { [super viewDidLoad]; [[ChildClass alloc] init]; }
- (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; }
@end
|
运行结果:
为什么会输出两句+[SuperClass initialize],而+[SuperClass load]只有一句呢?
原因很简单:
子类如果没有实现 load 的方法是不会去继承父类的方法实现的。而在子类中并没有关于 load 的定义,所以只输出一句 +[SuperClass load];
而 initialize 方法则不一样,子类没有定义 initialize 方法时,会使用父类的 initialize 方法定义。并且我们在第一次调用子类时,会先触发父类的 initialize 方法(第1句+[SuperClass initialize])。
一点点补充
load 调用时机比较早,运行环境有不确定因素。具体说来,在iOS上通常就是App启动时进行加载,但当load调用的时候,并不能保证所有类都加载完成且可用,所以在 load 做一些该类方法实现是比较危险的。
在 initialize 方法收到调用时,运行环境基本健全。
load 是在app载入时调用,所以不能做太多耗时的操作,否则十分影响用户体验。
总结
|
+ (void)load |
+ (void)initialize |
执行时机 |
在程序运行后立即执行 |
在类的方法第一次被调时执行 |
若自身未定义,是否沿用父类的方法? |
否 |
是 |
类别中的定义 |
全都执行,但后于类中的方法 |
覆盖类中的方法,只执行一个 |
端午节快乐~~!