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

+ (void)load{
    ZWWLog();
}

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

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

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();
//    }
//}

//会覆盖主类的init方法
//- (instancetype)init
//{
//    ZWWLog(@"TestClass (Method) init方法执行");
//    self = [super init];
//    if (self) {
//
//    }
//    return self;
//}

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

+ (void)load{
    ZWWLog();
}

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

//- (instancetype)init
//{
//    ZWWLog(@"TestClass (Method1) init方法执行");
//    self = [super init];
//    if (self) {
//        
//    }
//    return self;
//}

好了,代码准备完毕,下面在控制器中调用类,根据打印结果总结结论:

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

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

1.load方法测试:什么操作也不做,直接commond+b编译,打印如下:

2018-10-04 14:47:17.766168+0800 InterviewDemo[4064:619260] +[TestClass load] [Line 26]
2018-10-04 14:47:17.766824+0800 InterviewDemo[4064:619260] +[TestClassSon load] [Line 14]
2018-10-04 14:47:17.778301+0800 InterviewDemo[4064:619260] +[TestClass(Method) load] [Line 28]
2018-10-04 14:47:17.778399+0800 InterviewDemo[4064:619260] +[TestClass(method1) load] [Line 13]

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

在Bulid Phases->Compile Sources调整分类的编译顺序
oc中 load,initialize,init方法对比总结_第1张图片

打印结果:

2018-10-04 14:49:02.275479+0800 InterviewDemo[4101:628305] +[TestClass load] [Line 26]
2018-10-04 14:49:02.276066+0800 InterviewDemo[4101:628305] +[TestClassSon load] [Line 14]
2018-10-04 14:49:02.306659+0800 InterviewDemo[4101:628305] +[TestClass(method1) load] [Line 13]
2018-10-04 14:49:02.306799+0800 InterviewDemo[4101:628305] +[TestClass(Method) load] [Line 28]

可以看到两个分类的load方法调用顺序改变了

结论:

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

load方法相关要点:

  • 调用时机比较早,运行环境有不确定因素。具体说来,在iOS上通常就是App启动时进行加载,但当load调用的时候,并不能保证所有类都加载完成且可用,必要时还要自己负责做auto release处理。
  • 补充上面一点,对于有依赖关系的两个库中,被依赖的类的load会优先调用。但在一个库之内,调用顺序是不确定的。
  • 对于一个类而言,没有load方法实现就不会调用,不会考虑对NSObject的继承。
  • 一个类的load方法不用写明[super load],父类就会收到调用,并且在子类之前。
  • Category的load也会收到调用,但顺序上在主类的load调用之后。
  • 不会直接触发initialize的调用。
    load :可以说我们在日常开发中可以接触到的调用时间最靠前的方法,在主函数运行之前,load 方法就会调用。
    由于它的调用不是惰性的,且其只会在程序调用期间调用一次(所以runtime中交换方法的实现经常写在load方法中,可以确保方法仅交换一次),最最重要的是,如果在类与分类中都实现了 load 方法,它们都会被调用,不像其它的在分类中实现的方法会被覆盖,这就使 load 方法成为了方法调剂的绝佳时机。
    但是由于 load 方法的运行时间过早,所以这里可能不是一个理想的环境,因为某些类可能需要在在其它类之前加载,但是这是我们无法保证的。不过在这个时间点,所有的 framework 都已经加载到了运行时中,所以调用 framework 中的方法都是安全的。

load方法设深层理解及加载时机参考链接:
load方法深层理解

2.initialize和init方法测试:在控制器中输入以下代码(分类的initialize和init方法先注释掉):

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

打印结果:

2018-10-04 14:56:28.354678+0800 InterviewDemo[4231:658017] +[TestClass initialize] [Line 31] TestClass 中的initialize方法执行 class:TestClass
2018-10-04 14:56:28.354878+0800 InterviewDemo[4231:658017] -[TestClass init] [Line 36] TestClass 中的init方法执行 class:TestClass
2018-10-04 14:56:28.354968+0800 InterviewDemo[4231:658017] -[TestClass init] [Line 36] TestClass 中的init方法执行 class:TestClass
2018-10-04 14:56:28.355034+0800 InterviewDemo[4231:658017] -[TestClass init] [Line 36] TestClass 中的init方法执行 class:TestClass

结果:TestClass实例化3次,initialize执行1次,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];
                    TestClassSon *testClsSon3 = [[TestClassSon alloc]init];

情况1:父类,子类都实现了initialize,init方法,父类分类未实现initialize,init方法,打印结果

2018-10-04 14:59:38.183968+0800 InterviewDemo[4301:670394] +[TestClass initialize] [Line 31] TestClass 中的initialize方法执行 class:TestClass
2018-10-04 14:59:38.184082+0800 InterviewDemo[4301:670394] +[TestClassSon initialize] [Line 19]
2018-10-04 14:59:38.184158+0800 InterviewDemo[4301:670394] -[TestClassSon init] [Line 25]
2018-10-04 14:59:38.184244+0800 InterviewDemo[4301:670394] -[TestClass init] [Line 36] TestClass 中的init方法执行 class:TestClassSon
2018-10-04 14:59:38.184311+0800 InterviewDemo[4301:670394] -[TestClassSon init] [Line 25]
2018-10-04 14:59:38.184375+0800 InterviewDemo[4301:670394] -[TestClass init] [Line 36] TestClass 中的init方法执行 class:TestClassSon
2018-10-04 14:59:38.184432+0800 InterviewDemo[4301:670394] -[TestClassSon init] [Line 25]
2018-10-04 14:59:38.184494+0800 InterviewDemo[4301:670394] -[TestClass init] [Line 36] TestClass 中的init方法执行 class:TestClassSon

结果:
父类initialize,子类initialize都只执行1次,子类init,父类init各执行3次;
因为代码子类init方法中,后调用的 [super init](看下面代码),所以父类init在后面执行。将 [super init] 放到ZWWLog()前面,就会先调用父类init方法,下面是子类中的init方法实现

- (instancetype)init
{
   
    ZWWLog();
    return [super init];
}

情况2:父类实现initialize,init,子类未实现initialize(注释掉子类的方法),实现init,父类的分类未实现initialize,init情况,打印结果:

2018-10-04 15:02:50.767970+0800 InterviewDemo[4360:682382] +[TestClass initialize] [Line 31] TestClass 中的initialize方法执行 class:TestClass
2018-10-04 15:02:50.768151+0800 InterviewDemo[4360:682382] +[TestClass initialize] [Line 31] TestClass 中的initialize方法执行 class:TestClassSon
2018-10-04 15:02:50.768222+0800 InterviewDemo[4360:682382] -[TestClassSon init] [Line 25]
2018-10-04 15:02:50.768294+0800 InterviewDemo[4360:682382] -[TestClass init] [Line 36] TestClass 中的init方法执行 class:TestClassSon
2018-10-04 15:02:50.768348+0800 InterviewDemo[4360:682382] -[TestClassSon init] [Line 25]
2018-10-04 15:02:50.768401+0800 InterviewDemo[4360:682382] -[TestClass init] [Line 36] TestClass 中的init方法执行 class:TestClassSon
2018-10-04 15:02:50.768449+0800 InterviewDemo[4360:682382] -[TestClassSon init] [Line 25]
2018-10-04 15:02:50.768531+0800 InterviewDemo[4360:682382] -[TestClass init] [Line 36] TestClass 中的init方法执行 class:TestClassSon!

结果:父类执行2次initialize。1次父类本身的initialize,1次代替子类的initialize。父类和子类init方法各调用3次

情况3:父类,子类,父类的分类都实现initialize;父类、子类实现init,分类未实现init方法情况,打印结果:

2018-10-04 15:04:39.997736+0800 InterviewDemo[4406:691826] +[TestClass(Method) initialize] [Line 35]
2018-10-04 15:04:39.997908+0800 InterviewDemo[4406:691826] +[TestClassSon initialize] [Line 19]
2018-10-04 15:04:39.997983+0800 InterviewDemo[4406:691826] -[TestClassSon init] [Line 25]
2018-10-04 15:04:39.998105+0800 InterviewDemo[4406:691826] -[TestClass init] [Line 36] TestClass 中的init方法执行 class:TestClassSon
2018-10-04 15:04:39.998164+0800 InterviewDemo[4406:691826] -[TestClassSon init] [Line 25]
2018-10-04 15:04:39.998229+0800 InterviewDemo[4406:691826] -[TestClass init] [Line 36] TestClass 中的init方法执行 class:TestClassSon
2018-10-04 15:04:39.998286+0800 InterviewDemo[4406:691826] -[TestClassSon init] [Line 25]
2018-10-04 15:04:39.998348+0800 InterviewDemo[4406:691826] -[TestClass init] [Line 36] TestClass 中的init方法执行 class:TestClassSon

结果:父类的分类initialize执行1次(父类被分类覆盖掉),子类initialize执行1次,父类init,子类init各执行3次

情况4:父类,子类,父类的分类都实现initialize,init方法情况,打印结果:

2018-10-04 15:09:16.464298+0800 InterviewDemo[4483:707765] +[TestClass(Method) initialize] [Line 35]
2018-10-04 15:09:16.464429+0800 InterviewDemo[4483:707765] +[TestClassSon initialize] [Line 19]
2018-10-04 15:09:16.464502+0800 InterviewDemo[4483:707765] -[TestClassSon init] [Line 25]
2018-10-04 15:09:16.464615+0800 InterviewDemo[4483:707765] -[TestClass(Method) init] [Line 42] TestClass (Method) init方法执行
2018-10-04 15:09:16.464674+0800 InterviewDemo[4483:707765] -[TestClassSon init] [Line 25]
2018-10-04 15:09:16.464739+0800 InterviewDemo[4483:707765] -[TestClass(Method) init] [Line 42] TestClass (Method) init方法执行
2018-10-04 15:09:16.464784+0800 InterviewDemo[4483:707765] -[TestClassSon init] [Line 25]
2018-10-04 15:09:16.464854+0800 InterviewDemo[4483:707765] -[TestClass(Method) init] [Line 42] TestClass (Method) init方法执行

分类的编译顺序:
oc中 load,initialize,init方法对比总结_第2张图片
结果:分类initialize代替父类initialize执行一次,分类init代替父类init方法执行3次,子类init方法执行3次
综合上面四种情况得出结论:

  • 父类的+ initialize,-init方法和普通方法一样会被分类同名方法覆盖
  • 分类的同名方法加载顺序取决于编译的顺序:编译在“后面”的覆盖前面分类的方法(可以通过调换编译文件先后顺序来测试)
  • 父类子类均实现initialize,无论初始化init多少个实例,仅会在第一次使用到类时调用父类,子类initialize一次
  • 如果子类没有重写实现+ initialize方法,则第一次用到子类时调用父类+ initialize方法,如果父类分类实现了initialize,则调用分类的initialize

initialize方法相关要点

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

总结:
load和initialize有很多共同特点,下面简单列一下:

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

init和initialize区别:
initialize不是init,initialize在程序运行过程中,它会在你程序中每个类调用仅一次initialize。这个调用的时间发生在你的类接收到消息之前,但是在它的父类接收到initialize之后。
init则是你手动初始化几次就调用几次,和普通方法一样

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

你可能感兴趣的:(iOS开发原理)