iOS - load和initialize的区别

本文深入探讨了Objective-C中load和initialize方法的区别与应用场景。load方法在类被加载时调用,而initialize则在类的第一个方法被调用前执行。文章通过实例展示了这两种方法的调用顺序及它们与父类和子类之间的关系。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

有时候,类必须先执行某些初始化操作,然后才能正常使用。在Objective-C中,绝大多数都继承自NSObject这个根类,而该类有两个方法,可用来实现这种初始化操作。首先讲的是load()方法:

 

load()

class func load()

官方介绍

Invoked whenever a class or category is added to the Objective-C runtime;
implement this method to perform class-specific behavior upon loading.

当一个类或者分类被添加到oc的运行时会调用load方法,并且仅调用一次。我们可以实现这个方法执行具体的行为。简单来说就是当包含类或分类的程序库载入系统时,就会执行此方法,而这就是指应用程序启动的时候。

The load() message is sent to classes and categories that are both dynamically
loaded and statically linked, but only if the newly loaded class or category 
implements a method that can respond.

The order of initialization is as follows:

1、All initializers in any framework you link to.

2、All +load methods in your image.

3、All C++ static initializers and C/C++ __attribute__(constructor) functions in your image.

4、All initializers in frameworks that link to you.

In addition:

// 先调用父类的load方法在调用子类的load方法
1、A class’s +load method is called after all of its superclasses’ +load methods.

// 先调用本类load方法再调用分类load方法
2、A category +load method is called after the class’s own +load method.

In a custom implementation of load() you can therefore safely message other 
unrelated classes from the same image, but any load() methods implemented
by those classes may not have run yet.

Important

Custom implementations of the load method for Swift classes bridged to Objective-C
are not called automatically.

 

initialize()

 

class func initialize()

官方介绍

Initializes the class before it receives its first message.

在第一次接收消息之前初始化类。

The runtime sends initialize() to each class in a program just before the class, or any class that inherits from it, is sent its first message from within the program. Superclasses receive this message before their subclasses.

The runtime sends the initialize() message to classes in a thread-safe manner. That is, initialize() is run by the first thread to send a message to a class, and any other thread that tries to send a message to that class will block until initialize() completes.

The superclass implementation may be called multiple times if subclasses do not implement initialize()—the runtime will call the inherited implementation—or if subclasses explicitly call [super initialize]. If you want to protect yourself from being run multiple times, you can structure your implementation along these lines:

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

Because initialize() is called in a blocking manner, it’s important to limit method implementations to the minimum amount of work necessary possible. Specifically, any code that takes locks that might be required by other classes in their initialize() methods is liable to lead to deadlocks. Therefore, you should not rely on initialize() for complex initialization, and should instead limit it to straightforward, class local initialization.

文档中很清楚的说明了load和initialize的区别:load是只要类所在的文件被引用就会被调用,而initialize是在类或者其子类的第一个方法被调用前调用。所以如果类没有被引用进项目,就不会调用load方法,即使类文件被引用进来,如果没有使用,那么initialize不会被调用。

具体的例子分析

定义几个类Father,Son,InsideInitialize

@interface Father: NSObject
@end

@interface Son: Father
@end

@interface InsideInitialize: NSObject
- (void)objectMethod;
@end

对应类的实现如下

@implementation Father

+ (void)load {
    NSLog(@"%@ %s", [self class], __func__);
}

+ (void)initialize {
    NSLog(@"%@ %s", [self class], __func__);
}

@end

@implementation Son

+ (void)initialize {
    NSLog(@"%@ %s", [self class], __func__);
    InsideInitialize *object = [[InsideInitialize alloc]init];
    [object objectMethod];
}

@end

@implementation InsideInitialize

- (void)objectMethod {
    NSLog(@"%@ %s", [self class], __func__);
}

+ (void)initialize {
    NSLog(@"%@ %s", [self class], __func__);
}

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

@end

由代码可知:

1)Father类实现了load和initialize方法;

2)Son继承于Father类,但是只重写了initialize方法;并且在initialize中创建了InsideInitialize对象,调用了对应的方法

3)InsideInitialize类同样实现了load和initialize方法,外加一个实例方法;

除了InsideInitialize类的load方法是打印输出方法名,其余类中的函数都是打印输出类名和方法名。接下来仅仅只是运行程序,那么会得到如下结果:

 Father +[Father initialize]
 Father +[Father load]
 +[InsideInitialize load]

可以看到:

1)执行了Father类的initialize和load方法

2)执行了InsideInitialize类的load方法

为什么会这样呢?就像前面所说,只要项目中引入了那么运行时就会自动执行类的load方法。但是为什么Father的initialize方法会被执行呢?那是因为load方法中我们执行了[self class],这也就符合initialize的功能:在类的第一个方法被调用前执行,所以会先执行[Father initialize]再执行load方法。

 

load和initialize可被当作普通方法类被使用

在程序中直接使用Son类调用load方法:

 [Son load];

输出如下:

 Father +[Father initialize]
 Father +[Father load]
 +[InsideInitialize load]
 // excute load method
 Son +[Son initialize]
 InsideInitialize +[InsideInitialize initialize]
 InsideInitialize -[InsideInitialize objectMethod]
 Son +[Father load]

有结果可知,前面的3个结果和之前的一样,引入类到项目会自动执行load方法,后面的4个打印是执行了Son的load方法之后,因为initialize的功能是在类的第一个方法被调用前执行,所以会先执行Son的intialize方法,然后intialize方法中创建了InsideInitialize对象,因为是第一次执行了alloc类方法,所以会触发InsideInitialize类的intialize方法,然后调用InsideInitialize对象的实例方法。最后因为Son类本身并未实现load方法,所以会执行Father的oad方法。

注意:load和initialize方法被调用一次是相对runtime而言的,比如:Father的initialize方法不会因为自身存在load方法调用一次,又因为子类调用了load一次再去执行一次,只会执行一次的,所以我们可以直接反复调用这些方法。

 

子类会调用父类的initialize方法

接下来我们修改一下Father和Son类,去掉Father中的load方法,让Son来重写load方法,但是去掉initialize方法

@implementation Father

+ (void)initialize {
    NSLog(@"%@ %s", [self class], __func__);
}

@end

@implementation Son

+ (void)load {
    NSLog(@"%@ %s", [self class], __func__);
}

@end

依然什么也不干,简单引入这些类,运行程序,结果如下:

 Father +[Father initialize]
 Son +[Father initialize]
 Son +[Son load]
 +[InsideInitialize load]

可以看到最开始是直接因为自动执行Son的load方法,所以会先执行Son类并调用initialize方法,但是先执行父类的initialize方法。这也是Apple文档中所说,子类方法的调用会引起父类的initialize方法的调用。

 

类别(category)中的load和initialize方法

重新定义一个BaseClass类,并添加两个分类,如下:

@interface BaseClass: NSObject

@end

@implementation BaseClass

+ (void)load {
    NSLog(@"%@ %s", [self class], __func__);
}

+ (void)initialize {
    NSLog(@"%@ %s", [self class], __func__);
}

@end

@implementation BaseClass (FirstCategory)

+ (void)load {
    NSLog(@"%@ %s", [self class], __func__);
}

+ (void)initialize {
    NSLog(@"%@ %s", [self class], __func__);
}

@end

@implementation BaseClass (SecondCategory)

+ (void)load {
    NSLog(@"%@ %s", [self class], __func__);
}

+ (void)initialize {
    NSLog(@"%@ %s", [self class], __func__);
}

@end

引入类,并执行运行,结果如下:

BaseClass +[BaseClass(SecondCategory) initialize]
BaseClass +[BaseClass load]
BaseClass +[BaseClass(FirstCategory) load]
BaseClass +[BaseClass(SecondCategory) load]

1)initialize优于load被执行,但是只执行了最后一个分类中的initialize,其它的两个被隐藏了

2)对于load方法,三个都执行了,并且如Apple文档所说:先执行类自身,然后在执行分类的load方法。

3)注意一点:我们并不需要显示的使用super调用父类的load或者initialize方法,对于load方法系统会自动执行调用,对于initialize方法则会随着子类的调用而调用父类的方法。

 

总结

1、在加载阶段,如果类实现了 load 方法,那么系统就会调用它。分类里也可以定义此方法,类的 load 方法要比分类中的先调用。与其他方法不同,load 方法不参与覆写机制。

2、首次使用某个类之前,系统会向其发送 initialize 消息。由于此方法遵从普通的覆写规则,所以通常应该在里面判断当前要初始化的是哪个类

3、load 与 initialize 方法都应该实现的精简一些,这有助于保持应用程序的响应能力,也能减少引入 “保留环”(interdependency cycle)的几率。

4、无法在编译期设定的全局常量,可以放在 initialize 方法里初始化

 

参考:

Objective C类方法load和initialize的区别

Cocoa Application Startup

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值