iOS 类方法 +load 和 +initialize

2017-10-10  本文已影响187人  9d8c8692519b

iOS开发中总能看到+load和+initialize的身影,网上对于这两个方法有很多解释,官方也有说明,最近在看一个开源库的时候,发现有运用还是觉得需要根据理解整理一下。

负载函数 +load

Apple文档是这样描述的:

Invoked whenever a class or category is added to the Objective-C runtime; implement this method to perform class-specific behavior upon loading.
当类(Class)或者类别(Category)加入Runtime中时(就是被引用的时候)。
实现该方法,可以在加载时做一些类特有的操作。

Discussion
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:
All initializers in any framework you link to.
调用所有的Framework中的初始化方法
All +load methods in your image.
调用所有的+load方法
All C++ static initializers and C/C++ attribute(constructor) functions in your image.
调用C++的静态初始化方及C/C++中的attribute(constructor)函数
All initializers in frameworks that link to you.
调用所有链接到目标文件的framework中的初始化方法
In addition:
A class’s +load method is called after all of its superclasses’ +load methods.
一个类的+load方法在其父类的+load方法后调用
A category +load method is called after the class’s own +load method.
一个Category的+load方法在被其扩展的类的自有+load方法后调用
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.
在+load方法中,可以安全地向同一二进制包中的其它无关的类发送消息,但接收消息的类中的+load方法可能尚未被调用。

Apple文档地址: load

load函数调用特点如下:

当类被引用进项目的时候就会执行负载函数(在主函数开始执行之前),与这个类是否被用到无关,每个类的负载函数只会自动调用一次。由于负载函数是系统自动加载的,因此不需要调用父类的负载函数,否则父类的负载函数会多次执行。

1.当父类和子类都实现负载函数+load时,父类的负载方法+load执行顺序要优先于子类
2.当子类未实现负载方法+load时,不会调用父类负载方法+load
3.类中的负载方法+load执行顺序要优先于类别(类别)
当然有多个类别(类别)都实现了加载方法+load,这几个加载方法+load都会执行,但执行顺序不确定(其执行顺序与类别在编译源中出现的顺序一致)
5.当然当有多个不同的类的时候,每个类加载执行顺序与其在编译源出现的顺序一致

初始化 +initialize

苹果文档是这样描述的:

Initializes the class before it receives its first message.
在这个类接收第一条消息之前调用。
Discussion
The runtime sends initialize to each class in a program exactly one time just before the class, or any class that inherits from it, is sent its first message from within the program. (Thus the method may never be invoked if the class is not used.) The runtime sends the initialize message to classes in a thread-safe manner. Superclasses receive this message before their subclasses.
Runtime在一个程序中每一个类的一个程序中发送一个初始化一次,或是从它继承的任何类中,都是在程序中发送第一条消息。(因此,当该类不使用时,该方法可能永远不会被调用。)运行时发送一个线程安全的方式初始化消息。父类的调用一定在子类之前。

Apple文档地址: initialize

初始化函数调用特点如下:

初始化在类或者其子类的第一个方法被调用前调用。即使是类文件被引用进项目,但是没有使用,初始化不会被调用。由于是系统自动调用,也不需要再调用[super initialize] ,否则父类的初始化会被多次执行。假如这个类放到代码中,而这段代码并没有被执行,这个函数是不会被执行的。

1.父类的初始化方法会比子类先执行
2.当子类未实现初始化方法时,会调用父类初始化方法,子类实现初始化方法时,会覆盖父类初始化方法。
3.当有多个类别都实现了初始化方法,会覆盖类中的方法,只执行一个(会执行编译源列表中最后一个类别的初始化方法)

什么情况下使用:

+load

由于调用负载方法时的环境很不安全,我们应该尽量减少负载方法的逻辑。另一个原因是负载方法是线程安全的,它内部使用了锁,所以我们应该避免线程阻塞在负载方法中

加载很常见的一个使用场景,交换两个方法的实现:

//摘自MJRefresh
+ (void)load
{
    [self exchangeInstanceMethod1:@selector(reloadData) method2:@selector(mj_reloadData)];
    [self exchangeInstanceMethod1:@selector(reloadRowsAtIndexPaths:withRowAnimation:) method2:@selector(mj_reloadRowsAtIndexPaths:withRowAnimation:)];
    [self exchangeInstanceMethod1:@selector(deleteRowsAtIndexPaths:withRowAnimation:) method2:@selector(mj_deleteRowsAtIndexPaths:withRowAnimation:)];
    [self exchangeInstanceMethod1:@selector(insertRowsAtIndexPaths:withRowAnimation:) method2:@selector(mj_insertRowsAtIndexPaths:withRowAnimation:)];
    [self exchangeInstanceMethod1:@selector(reloadSections:withRowAnimation:) method2:@selector(mj_reloadSections:withRowAnimation:)];
    [self exchangeInstanceMethod1:@selector(deleteSections:withRowAnimation:) method2:@selector(mj_deleteSections:withRowAnimation:)];
    [self exchangeInstanceMethod1:@selector(insertSections:withRowAnimation:) method2:@selector(mj_insertSections:withRowAnimation:)];
}

+ (void)exchangeInstanceMethod1:(SEL)method1 method2:(SEL)method2
{
    method_exchangeImplementations(class_getInstanceMethod(self, method1), class_getInstanceMethod(self, method2));
}

+initialize

初始化方法主要用来对一些不方便在编译期初始化的对象进行赋值比如NSMutableArray的这种类型的实例化依赖于运行时的消息发送,所以显然无法在编译器初始化:

// In Person.m
// int类型可以在编译期赋值
static int someNumber = 0; 
static NSMutableArray *someArray;
+ (void)initialize {
    if (self == [Person class]) {
        // 不方便编译期复制的对象在这里赋值
        someArray = [[NSMutableArray alloc] init];
    }
}

总结:

加载和初始化的共同点

如果父类和子类都被调用,父类的调用一定在子类之前

+load方法要点

当类被引用进项目的时候就会执行负载函数(在主函数开始执行之前),与这个类是否被用到无关,每个类的负载函数只会自动调用一次。由于负载函数是系统自动加载的,因此不需要再调用[super load],否则父类的负载函数会多次执行。

1.当父类和子类都实现负载函数时,父类的负载方法执行顺序要优先于子类
2.当一个类未实现负载方法时,不会调用父类负载方法
3.类中的负载方法执行顺序要优先于类别(类别)
当然有多个类别(类别)都实现了加载方法,这几个加载方法都会执行,但执行顺序不确定(其执行顺序与类别在编译源中出现的顺序一致)
5.当然当有多个不同的类的时候,每个类加载执行顺序与其在编译源出现的顺序一致
注意:负载调用时机比较早,当负载调用时,其他类可能还没加载完成,运行环境不安全。加载方法是线程安全的,它使用了锁,我们应该避免线程阻塞在负载方法。

+initialize方法要点

初始化在类或者其子类的第一个方法被调用前调用。即使类文件被引用进项目,但是没有使用,初始化不会被调用。由于是系统自动调用,也不需要显式的调用父类的初始化,否则父类的初始化会被多次执行。假如这个类放到代码中,而这段代码并没有被执行,这个函数是不会被执行的。

1.父类的初始化方法会比子类先执行
2.当子类不实现初始化方法,会把父类的实现继承过来调用一遍。在此之前,父类的方法会被优先调用一次
3.当有多个类别都实现了初始化方法,会覆盖类中的方法,只执行一个(会执行编译源列表中最后一个类别的初始化方法)

注意:在初始化方法收到调用时,运行环境基本健全。初始化内部也使用了锁,所以是线程安全的。但同时要避免阻塞线程,不要再使用锁

附:

关注我,获取更多精彩的iOS 开发内容。
内容学习整理源于 it7090 朱晓辉

上一篇下一篇

猜你喜欢

热点阅读