oc中 load,initialize,init方法对比总结

首先代码展示吧,后面也会附赠demo代码链接

1.定义测试类TestClass
//为了方便打印,先在宏定义文件里面定义ZWWLog

#ifdef DEBUG
#define ZWWLog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);
#else
#define ZWWLog(...)
#endif

TestClass.m

- (instancetype)init{
    ZWWLog(@"TestClass 中的init方法执行 class:%@",[self class]);
    return [super init]; 
}

+ (void)load{
     ZWWLog();
}

+ (void)initialize
{
     ZWWLog(@"TestClass 中的initialize方法执行 class:%@",[self class]);
}

2.定义测试类TestClass的子类TestClassSon

TestClassSon.m

+ (void)load{
    ZWWLog();
}

+ (void)initialize
{
    ZWWLog();
}

- (instancetype)init
{

    ZWWLog();
    return [super init];
}

2.定义测试类TestClass的分类1TestClass (Method)

//不会覆盖主类的load
+ (void)load{
    ZWWLog();
}

//会覆盖主类的initialize
//+ (void)initialize
//{
//    if (self == [self class]) {
//        ZWWLog();
//    }
//}

2.定义测试类TestClass的分类2TestClass (Method1)

+ (void)load{
    ZWWLog();
}

//+ (void)initialize
//{
//    if (self == [self class]) {
//        ZWWLog();
//    }
//}

好了,下面在控制器中调用类,根据打印结果总结结论:

在控制器中需要导入对应头文件

#import "TestClass.h"
#import "TestClassSon.h"
#import "TestClass+Method.h"
#import "TestClass+method1.h"

1.什么操作也不做,此时就编译,打印如下:
这里写图片描述

在Bulid Phases->Compile Sources调整分类的编译顺序
调整编译顺序图

打印结果:
这里写图片描述
如果你导入别的头文件,重写load方法进行打印的话,同样也可以看到该类load方法被执行

结论:

  • +load方法的调用是在main() 函数之前,并且不需要主动调用,就是说程序启动会把所有的文件加载
    主类和分类的都会加载调用+load方法
  • 主类与分类的加载顺序是:主类优先于分类加载,无关编译顺序
  • 分类间的加载顺序取决于编译的顺序:编译在“前”则先加载,编译在后则后加载
  • 规则是:父类优先于子类, 子类优先于分类(父类>子类>分类)

load方法相关要点:

  • 调用时机比较早,运行环境有不确定因素。具体说来,在iOS上通常就是App启动时进行加载,但当load调用的时候,并不能保证所有类都加载完成且可用,必要时还要自己负责做auto release处理。
  • 补充上面一点,对于有依赖关系的两个库中,被依赖的类的load会优先调用。但在一个库之内,调用顺序是不确定的。
  • 对于一个类而言,没有load方法实现就不会调用,不会考虑对NSObject的继承。
  • 一个类的load方法不用写明[super load],父类就会收到调用,并且在子类之前。
  • Category的load也会收到调用,但顺序上在主类的load调用之后。
  • 不会直接触发initialize的调用。

load :可以说我们在日常开发中可以接触到的调用时间最靠前的方法,在主函数运行之前,load 方法就会调用。

由于它的调用不是惰性的,且其只会在程序调用期间调用一次,最最重要的是,如果在类与分类中都实现了 load 方法,它们都会被调用,不像其它的在分类中实现的方法会被覆盖,这就使 load 方法成为了方法调剂的绝佳时机。

但是由于 load 方法的运行时间过早,所以这里可能不是一个理想的环境,因为某些类可能需要在在其它类之前加载,但是这是我们无法保证的。不过在这个时间点,所有的 framework 都已经加载到了运行时中,所以调用 framework 中的方法都是安全的。

load方法设深层理解及加载时机参考链接:
load方法深层理解
2.在控制器中输入以下代码:

TestClass *testCls1 = [[TestClass alloc]init];
TestClass *testCls2 = [[TestClass alloc]init];
TestClass *testCls3 = [[TestClass alloc]init];

打印结果:
这里写图片描述
结果:initialize执行一次,init方法执行3次

替换输入以下代码:

//TestClass *testCls1 = [[TestClass alloc]init];
//TestClass *testCls2 = [[TestClass alloc]init];
//TestClass *testCls3 = [[TestClass alloc]init];
TestClassSon *testClsSon1 = [[TestClassSon alloc]init];
TestClassSon *testClsSon2 = [[TestClassSon alloc]init];

打印结果
这里写图片描述

结果:父类initialize执行一次->子类initialize执行一次,子类init,父类init各执行两次,因为代码子类init方法中,后调用的 [super init],所以父类init在后面执行。将 [super init] 放到ZWWLog()前面,就会先调用父类init方法

- (instancetype)init
{

    ZWWLog();
    return [super init];
}

如果子类不实现initialize方法,打印结果:
这里写图片描述
结果:调用两次父类的initialize,一次父类本身的initialize,一个代替子类的initialize。父类和子类init方法各调用两次

如果其中多个分类、子类、父类也都实现了initialize方法,打印结果:
这里写图片描述

结论:

  • 当第一次用到类的时候, 如果重写了+ initialize方法,会去调用一次
  • 如果子类没有重写实现+ initialize方法,则调用父类+ initialize方法
  • 当调用子类的+ initialize方法时候, 先调用父类的,如果父类有分类, 那么分类的+ initialize会覆盖掉父类的, 和普通方法差不多:分类间的加载顺序取决于编译的顺序:编译在“后面”的覆盖前面分类的方法,也可以通过调换编译文件先后顺序来测试
  • 父类的+ initialize不一定会调用, 因为分类可能重写了它
  • 优先级跟普通方法类似:分类>子类>父类

initialize方法相关要点
- initialize的自然调用是在第一次主动使用当前类的时候。
- 在initialize方法收到调用时,运行环境基本健全。
- initialize的运行过程中是能保证线程安全的。
- 和load不同,即使子类不实现initialize方法,会把父类的实现继承过来调用一遍。注意的是在此之前,父类的方法已经被执行过一次了,同样不需要super调用。

总结:
load和initialize有很多共同特点,下面简单列一下:
- 在不考虑开发者主动使用的情况下,系统最多会调用一次
- 如果父类和子类都被调用,父类的调用一定在子类之前
- 都是为了应用运行提前创建合适的运行环境
-在使用时都不要过重地依赖于这两个方法,除非真正必要

init和initialize区别:

initialize不是init:在程序运行过程中,它会在你程序中每个类调用一次initialize。这个调用的时间发生在你的类接收到消息之前,但是在它的父类接收到initialize之后。

下面是demo代码形式验证常见面试原理问题链接,有问题欢迎指正,探讨~~
demo代码

猜你喜欢

转载自blog.csdn.net/wei371522/article/details/81207863