+load与+initialize

一、initialize

1、在程序中向一个类或者它的子类第一次发消息的之前,runtime会向该类发送initialize消息。
2、父类在其子类之前接收initialize消息。如果superclass之前没有收到过initialize消息,会首先调用super class的initialize,然后才当前class的initialize。

3、如果子类没有实现initialize方法,或者子类显式调用[super initialize],superclass的实现会被多次调用。
4、runtime以线程安全的方式将initialize消息发送给类。也就是说,initialize由第一个线程运行,以向类发送消息,并且任何试图向该类发送消息的线程都将阻塞,直到initialize完成。
5、如果您希望保护自己不被多次运行,您可以按照以下的方式来构造您的方法:

+ (void)initialize {
  if (self == [ClassName self]) {
    // ... do the initialization ...
  }
}

6、因为initialize是以阻塞的方式被调用,所以在initialize方法中不适宜做过于繁重的初始化工作。特别注意,如果加锁的代码在其他类的initialize方法中调用有可能会导致死锁。因此,不应该依赖于initialize来进行复杂的初始化,而应该完成一些简单的本地初始化。
7、initialize在每个类中只调用一次,如果在category中重写了initialize方法,会覆盖当前类的initialize方法。如果您想为类和类别执行独立的初始化,您应该实现+load方法。

二、load

在类或类别添加到runtime时调用;在这个方法中可以执行一些特定的行为。比如初始化一个全局的表,并将类注册到其中。

#不像initialize,不存在重复调用的情况。
+ (void)load;

初始化过程:
所有链接的framework的initializer

所有+load方法

本程序的所有C++static initializer 和C/C++ attribute(constructor)方法

所有链接本程序的framework的initializer
【注】
当所有super class的load方法调用完成,才执行当前类的load方法
当前class的load方法调用完成,才执行当前category的load方法

三、示例

1、

#本文中用到的示例,大家可以分别注释子类或是父类的initialize或是load方法,运行看看有什么不同#

#Father:父类
#import 
@interface Father : NSObject
@end

#import "Father.h"
@implementation Father
+ (void)initialize{
    NSLog(@"%s",__func__);
}

+ (void)load{
    NSLog(@"%s",__func__);
}
@end

#Son1:继承Father的子类
#import "Father.h"
@interface Son1 : Father
@end

#import "Son1.h"
@implementation Son1
+ (void)initialize{
    NSLog(@"%s",__func__);
}
+ (void)load{
    NSLog(@"%s",__func__);
}
@end

#Son2:继承Father的子类
#import "Father.h"
@interface Son2 : Father
@end

#import "Son2.h"
@implementation Son2
+ (void)initialize{
    NSLog(@"%s",__func__);
}

+ (void)load{
    NSLog(@"%s",__func__);
}
@end

#Son1+Load: Son1的分类
#import "Son1.h"
@interface Son1 (Load)
@end

#import "Son1+Load.h"
@implementation Son1 (Load)
+ (void)initialize{
    NSLog(@"%s",__func__);
}
+ (void)load{
    NSLog(@"%s",__func__);
}
@end

我们只是创建了几个类,什么都不做,运行一下

2018-02-26 12:54:23.054726+0800 runtimeDemo[31256:5838555] +[Father load]
2018-02-26 12:54:23.055196+0800 runtimeDemo[31256:5838555] +[Son1 load]
2018-02-26 12:54:23.055801+0800 runtimeDemo[31256:5838555] +[Son2 load]
2018-02-26 12:54:23.055879+0800 runtimeDemo[31256:5838555] +[Son1(Load) load]

2、若子类没有实现initialize

#import "Son2.h"
@implementation Son2
//+ (void)initialize{
//    NSLog(@"%s",__func__);
//}
+ (void)load{
    NSLog(@"%s",__func__);
}
@end
#import "ViewController.h"
#import "Son2.h"
@interface ViewController ()

@end
@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    [Son2 new];
}

控制台

2018-02-26 13:30:13.318882+0800 runtimeDemo[31794:5867109] +[Father load]
2018-02-26 13:30:13.319541+0800 runtimeDemo[31794:5867109] +[Son1 load]
2018-02-26 13:30:13.319988+0800 runtimeDemo[31794:5867109] +[Son2 load]
2018-02-26 13:30:13.320241+0800 runtimeDemo[31794:5867109] +[Son1(Load) load]

2018-02-26 13:30:13.992162+0800 runtimeDemo[31794:5867109] +[Father initialize]
2018-02-26 13:30:13.992247+0800 runtimeDemo[31794:5867109] +[Father initialize]

3、+load 执行时机

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    [Father new];
    [Son1 new];
    [Son2 new];
}
@end

控制台

2018-02-26 13:37:48.911794+0800 runtimeDemo[32019:5876718] +[Father load]
2018-02-26 13:37:48.912212+0800 runtimeDemo[32019:5876718] +[Son1 load]
2018-02-26 13:37:48.912451+0800 runtimeDemo[32019:5876718] +[Son2 load]
2018-02-26 13:37:48.912835+0800 runtimeDemo[32019:5876718] +[Son1(Load) load]
2018-02-26 13:37:48.912950+0800 runtimeDemo[32019:5876718] main
2018-02-26 13:37:49.892705+0800 runtimeDemo[32019:5876718] -[AppDelegate application:didFinishLaunchingWithOptions:]
2018-02-26 13:37:49.894483+0800 runtimeDemo[32019:5876718] +[Father initialize]
2018-02-26 13:37:49.894563+0800 runtimeDemo[32019:5876718] +[Son1(Load) initialize]
2018-02-26 13:37:49.894652+0800 runtimeDemo[32019:5876718] +[Son2 initialize]

【总结】
1、+load 类加载到runtime就会调用,按照父类,子类,分类的顺序进行加载。
2、若有多个子类,加载顺序依照building setting中compile sources 中的子类顺序进行加载
3、若父类子类均有分类,加载顺序依照building setting中compile sources 中的分类先后顺序进行加载。

多个子类&多个分类

compile sourcese

加载顺序

2018-02-26 13:44:34.522342+0800 runtimeDemo[32225:5885402] +[Father load]
2018-02-26 13:44:34.523200+0800 runtimeDemo[32225:5885402] +[Son2 load]
2018-02-26 13:44:34.523312+0800 runtimeDemo[32225:5885402] +[Son1 load]
2018-02-26 13:44:34.523576+0800 runtimeDemo[32225:5885402] +[Son2(Load) load]
2018-02-26 13:44:34.523720+0800 runtimeDemo[32225:5885402] +[Son1(Load) load]
2018-02-26 13:44:34.523912+0800 runtimeDemo[32225:5885402] +[Father(Load) load]
2018-02-26 13:44:34.524117+0800 runtimeDemo[32225:5885402] main
2018-02-26 13:44:35.516505+0800 runtimeDemo[32225:5885402] -[AppDelegate application:didFinishLaunchingWithOptions:]

4、initialize只有在用到类的时候才会被调用。
5、父类initialize可能会被调用多次。

四、应用场景

1、+load方法中完成swizzing method。
2、统跳协议模式的模块化工程,每个类在+load方法中注册当前类可以相应的链接。
3、等等

以上,掌握这两个方法在程序加载的时候做一些初始化的工作。
如果有不准确的地方,欢迎批评指正。

你可能感兴趣的:(+load与+initialize)