iOS - +load 和 + initialize的区别

1,358 阅读2分钟

1. +load 和+initialize调用时机次数

声明类PersonPerson+Category

//
//  Person.m
//  LoadInitializeDemo
//
//  Created by Ternence on 2021/5/10.
//

#import "Person.h"

@implementation Person
+ (void)load {
    [super load];
    
    NSLog(@"\n Person +load \n ");
}

+ (void)initialize {
    [super initialize];
    NSLog(@"\n Person +initialize");
}

@end

//
//  Person+Category.m
//  LoadInitializeDemo
//
//  Created by Ternence on 2021/5/10.
//

#import "Person+Category.h"

@implementation Person (Category)

+ (void)load {
    [super load];
    NSLog(@"\n Person Category +load \n ");
}

+ (void)initialize {
    [super initialize];
    NSLog(@"\n Person Category +initialize \n ");
}
@end

不调用不引用不用该类创建实例对象,启动App。查看打印

2021-05-10 14:47:05.534059+0800 LoadInitializeDemo[37437:645704] 
 Person +load
 Person Category +load

在其它类创建一个Person实例:

- (void)viewDidLoad {
    [super viewDidLoad];
    
    Person *person1 = [[Person alloc] init];
    NSLog(@"person1 created");
    Person *person2 = [[Person alloc] init];
    NSLog(@"person2 created");
}

查看打印:

 Person +load
 Person Category +load
 Person Category +initialize
 person1 created 
 person2 created

对比两次打印结果,我们可以得出结论:

  1. 创建该类后不创建实例,发现+load调用了,且在main函数之前调用,+initialize并未调用
  2. 首次创建该类的实例的时候+initialize调用了,之后再没有调用
  3. 类和分类的+load都会在main函数之前调用
  4. +load 和+initialize 都只调用一次

2. 父类子类调用 +load 和 +initialize的先后顺序

创建StudentStudent+Category 继承自Person,不引用不调用PersonStudent,查看代码打印

2021-05-10 15:23:45.758752+0800 LoadInitializeDemo[59531:714585] 
 Person +load
 Person Category +initialize
 Person Category +load
 Student +load
 Person Category +load
 Person Category +load
 Student Category +load

结论:

+load

  1. 先调用类的+load,后调用分类的+load
  2. 先调用父类+load,后调用子类的+load
  3. 按照编译顺序调用+load(先编译先调用) +initialize
  4. 先初始化分类,后初始化子类
  5. 先调用父类,后调用子类

3. 总结

  1. load是runtime加载类、分类的时候调用(只会调用一次)
  2. initialize是类第一次接收到消息的时候调用, 每一个类只会initialize一次(如果子类没有实现initialize方法, 会调用父类的initialize方法, 所以父类的initialize方法可能会调用多次)

4. +load使用场景

Runtime Swizzing 方法时,应该在+load方法中调用,因为此方法在类编译后,main函数之前,切只调用依次

5. +load源码阅读

官方源码地址:objc4 调用顺序:查看objc-os.mm

  • _objc_init
  • load_images
  • prepare_load_methods
    • schedule_class_load
    • add_class_to_loadable_list
    • add_category_to_loadable_list
  • call_load_methods
    • call_class_loads
    • call_category_loads
    • load_method_t

6. +initialize源码阅读

调用顺序:

  • objc-msg-arm64.s
    • _objc_msgSend
  • objc-runtime-new.mm
    • class_getInstanceMethod
    • lookUpImpOrForward
    • __class_initialize
    • callInitialize
    • objc_msgSend