iOS - load和initialize的区别

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:

1、A class’s +load method is called after all of its superclasses’ +load methods.

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方法。但是为什么Fatherinitialize方法会被执行呢?那是因为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方法则会随着子类的调用而调用父类的方法。


总结:

  load initialize
执行时机 在程序运行后执行 在类的方法第一次调用时执行
如果自身未定义,是否沿用父类的方法?
类别中的定义 全部都执行,但是后于类中的方法 覆盖类中的方法,只执行一个


参考:

Objective C类方法load和initialize的区别

Cocoa Application Startup



猜你喜欢

转载自blog.csdn.net/longshihua/article/details/80189654