iOS - + initialize 与 +load

作者: Mitchell | 来源:发表于2015-10-27 15:53 被阅读13423次

Objective-C 有两个神奇的方法:+load 和 +initialize,这两个方法在类被使用时会自动调用。但是两个方法的不同点会导致应用层面上性能的显著差异。

一、+ initialize 方法和+load 调用时机

  • 首先说一下 + initialize 方法:苹果官方对这个方法有这样的一段描述:这个方法会在 第一次初始化这个类之前 被调用,我们用它来初始化静态变量。
    • load 方法会在加载类的时候就被调用,也就是 ios 应用启动的时候,就会加载所有的类,就会调用每个类的 + load 方法。
  • 之后我们结合代码来探究一下 + initialize 与 + load 两个方法的调用时机,首先是 + load
#pragram ---main函数中的代码---
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int main(int argc, char * argv[]) {
    NSLog(@"%s",__func__);
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}
#pragram ---基于NSObject的Person类---
#import "Person.h"
@implementation Person
 + (void)load{
    NSLog(@"%s",__func__);
}
 + (void)initialize{
    [super initialize];
    NSLog(@"%s %@",__func__,[self class]);
}
 - (instancetype)init{
    if (self = [super init]) {
        NSLog(@"%s",__func__);
    }
    return self;
}
@end
#pragram ---基于Person的Son类---
#import "Girl.h"
@implementation Girl
 + (void)load{
    NSLog(@"%s ",__func__);
}
 + (void)initialize{
    [super initialize];
    NSLog(@"%s ",__func__);
}
 - (instancetype)init{
    if (self = [super init]) {
        NSLog(@"%s",__func__);
    }
    return self;
}
@end

运行程序,我们看一下输出日志:

2015-10-27 15:21:07.545 initialize[11637:334237] +[Person load]
2015-10-27 15:21:07.546 initialize[11637:334237] +[Girl load] 
2015-10-27 15:21:07.546 initialize[11637:334237] main

这说明在我并没有对类做任何操作的情况下,+load 方法会被默认执行,并且是在 main 函数之前执行的。

  • 接下来我们来查看一下 + initialize 方法,先在 ViewController 中创建 Person 和 Girl 对象:
#import "ViewController.h"
#import "Person.h"
#import "Son.h"
#import "Girl.h"
@interface ViewController ()
@end
@implementation ViewController
 - (void)viewDidLoad {
    [super viewDidLoad];
    Person * a = [Person new];
    Person * b = [Person new];
    Girl *c = [Girl new];
    Girl *d = [Girl new];
}
@end

下面我们来看一下输出日志:

2015-10-27 15:33:56.195 initialize[11711:342410] +[Person load]
2015-10-27 15:33:56.196 initialize[11711:342410] +[Girl load] 
2015-10-27 15:33:56.197 initialize[11711:342410] main
2015-10-27 15:33:56.259 initialize[11711:342410] +[Person initialize] Person
2015-10-27 15:33:56.259 initialize[11711:342410] -[Person init]
2015-10-27 15:33:56.259 initialize[11711:342410] -[Person init]
2015-10-27 15:33:56.259 initialize[11711:342410] +[Girl initialize] 
2015-10-27 15:33:56.260 initialize[11711:342410] -[Girl init]
2015-10-27 15:33:56.260 initialize[11711:342410] -[Girl init]

通过这个实验我们可以确定两点:
- + initialize 方法类似一个懒加载,如果没有使用这个类,那么系统默认不会去调用这个方法,且默认只加载一次;
- + initialize 的调用发生在 +init 方法之前。

  • 接下来再探究一下 + initialize 在父类与子类之间的关系,创建一个继承自 Person 类的 Son类:
#pragram ---ViewController 中的代码---
#import "ViewController.h"
#import "Person.h"
#import "Son.h"
#import "Girl.h"
@interface ViewController ()
@end
@implementation ViewController
 - (void)viewDidLoad {
    [super viewDidLoad];
    Person * a = [Person new];
    Person * b = [Person new];
    Son*z = [Son new];
}
@end

看一下输出日志:

2015-10-27 15:44:55.762 initialize[12024:351576] +[Person load]
2015-10-27 15:44:55.764 initialize[12024:351576] +[Son load]
2015-10-27 15:44:55.764 initialize[12024:351576] +[Girl load] 
2015-10-27 15:44:55.764 initialize[12024:351576] main
2015-10-27 15:44:55.825 initialize[12024:351576] +[Person initialize] Person
2015-10-27 15:44:55.825 initialize[12024:351576] -[Person init]
2015-10-27 15:44:55.825 initialize[12024:351576] -[Person init]
2015-10-27 15:44:55.826 initialize[12024:351576] +[Person initialize] Son
2015-10-27 15:44:55.826 initialize[12024:351576] -[Person init]

我们会发现 Person 类的 + initialize 方法又被调用了,但是查看一下是子类 Son 调用的,也就是创建子类的时候,子类会去调用父类的 + initialize 方法。

二、总结

  • 如果你实现了 + load 方法,那么当类被加载时它会自动被调用。这个调用非常早。如果你实现了一个应用或框架的 + load,并且你的应用链接到这个框架上了,那么 + load 会在 main() 函数之前被调用。如果你在一个可加载的 bundle 中实现了 + load,那么它会在 bundle 加载的过程中被调用。
  • + initialize 方法的调用看起来会更合理,通常在它里面写代码比在 + load 里写更好。+ initialize 很有趣,因为它是懒调用的,也有可能完全不被调用。类第一次被加载时,
  • + initialize 不会被调用。类接收消息时,运行时会先检查 + initialize 有没有被调用过。如果没有,会在消息被处理前调用。

相关文章

网友评论

  • 跟着内心走:为啥我按照楼主的代码执行的时候,initialize在main之前调用的呢?
    搬运工开发者:因为你手动实现load方法,试试把类的load方法注释,initialize就会在类第一次接收到消息时,就会调用
  • 54a558c9aee8:各位能告诉我 在Xcode中怎么创建这种类文件吗?
  • 14bf7199f818:好文!已收入《iOS - 大杂烩》《iOS - 线程/底层》专题,感谢作者的分享!
    ———————————————————————————————
    不定期更新高质量文章,欢迎投稿关注,更多iOS专项专题:
    http://www.jianshu.com/c/563ba4d42d38[iOS - 大杂烩]
    http://www.jianshu.com/c/1fee819318a7[iOS - 内存/数据/存储]
    http://www.jianshu.com/c/6a28d1d173b5[iOS - 线程/底层]
    http://www.jianshu.com/c/4dd5a67bbe1d[iOS - 音视频/图文/动画]
    http://www.jianshu.com/c/2de7446d0de9[iOS - 网络处理]
    http://www.jianshu.com/c/5458bca566a9[iOS - 安全/逆向]
    http://www.jianshu.com/c/ca237eb95714[iOS - 优化/质量]
  • 我本善良:补充:
    1.load方法的调用时机,main函数之前,先调用类中的,再调用类别中的(类别中如果有重写);
    2.initialize方法的调用时机,当向该类发送第一个消息(一般是类消息首先调用,常见的是alloc)的时候,先调用类中的,再调用类别中的(类别中如果有重写);如果该类只是引用,没有调用,则不会执行initialize方法。
    两者方法的共同点:自动调用父类的,不需要super操作;自动调用仅仅会调用一次(不包括外部显示调用)。
    :smile:
    TaoGeNet:@田村kafka 看这个源码,调用形式不一样,一个是函数指针形式调用load,一个是消息方式调用initialize所以类别中重写load不会覆盖
    2a6f767eace1:load方法调用顺序是:先本类再类别,不会覆盖
    initialize的调用顺序是:先类别,会覆盖本类的
    f886718d8e26:类别中重写的load不会覆盖原类的load方法吗? 难道说load和其它方法不一样
  • 响尾猫:好面试题
  • 萌猫大象:好文,赞一个!
  • helloDolin:不做,赞一个

本文标题:iOS - + initialize 与 +load

本文链接:https://www.haomeiwen.com/subject/abmthttx.html