OC load 和 initialize 方法
OC中有两个特殊的类方法,分别是load
和initialize
。
先来看看NSObject Class Reference里对这两个方法说明:
+(void)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.// 只要类或类别实现了load方法, 不论这个类或类别是动态加载还是静态链接, 类/类别都会受到load消息。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.- A category
+load
method is called after the class’s own+load
method.- 先父类load-> 在子类load->最后category中load(父类的category优先级也低于子类)
In a custom implementation of
load
you can therefore safely message other unrelated classes from the same image, but anyload
methods implemented by those classes may not have run yet. // 在自定义的实现+load方法中,可以安全地向同一二进制包中的其它无关的类发送消息,但接收消息的类中的+load方法可能尚未被调用。+(void)initialize
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人会给每一个类
发送initialize消息,
先父类在子类,initialize会
保证线程安全
Apple的文档很清楚地说明了initialize和load的区别在于:load是只要类所在文件被引用就会被调用,而initialize是在类或者其子类的第一个方法被调用前调用。所以如果类没有被引用进项目,就不会有load调用;但即使类文件被引用进来,但是没有使用,那么initialize也不会被调用。
load
顾名思义,load
方法在这个文件被程序装载时调用。只要是在Compile Sources中出现的文件总是会被装载,这与这个类是否被用到无关,load
方法总是在main
函数之前调用。
调用规则
如果一个类实现了load
方法,在调用这个方法前会首先调用父类的load
方法。而且这个过程是自动完成的,并不需要我们手动实现,由于load函数是系统自动加载的,因此不需要再调用[super load],否则父类的load函数会多次执行。:
// In Parent.m
+ (void)load {NSLog(@"Load Class Parent");
}// In Parent+load.m
+ (void)load {NSLog(@"Load Class Parent+load");
}// In Child.m,继承自Parent
+ (void)load {NSLog(@"Load Class Child");
}// In Child+load.m,Child类的分类
+ (void)load {NSLog(@"Load Class Child+load");
}// 运行结果: Parent+load和Child+load的顺序和文件编译顺序有关
/*2016-02-01 21:28:14.379 load[11789:1435378] Load Class Parent2016-02-01 21:28:14.380 load[11789:1435378] Load Class Child2016-02-01 21:28:14.380 load[11789:1435378] Load Class Parent+load2016-02-01 22:28:14.381 load[11789:1435378] Load Class Child+load
*/
执行顺序
load
方法调用时,系统处于脆弱状态,如果调用别的类的方法,且该方法依赖于那个类的load
方法进行初始化设置,那么必须确保那个类的load
方法已经调用了,比如demo中的这段代码,打印出的字符串就为null
:
// In Child.m
+ (void)load {NSLog(@"Load Class Child");Other *other = [Other new];[other originalFunc];// 如果不先调用other的load,下面这行代码就无效,打印出null[Other printName];
}
load
方法的调用顺序其实有迹可循,在Compile Sources中,文件的排放顺序就是其装载顺序,自然也就是load
方法调用的顺序。这一点也证明了load
方法中会自动调用父类的方法,因为在demo的输出结果中,Parent
的load
方法先于Child
调用,而它的装载顺序其实在Child
之后。
虽然在这种简单情况下我们可以辨别出各个类的load
方法调用的顺序,但永远不要依赖这个顺序完成你的代码逻辑。一方面,这在后期的开发中极容易导致错误,另一方面,你实际上并不需要这么做。
使用场景
由于调用load
方法时的环境很不安全,我们应该尽量减少load
方法的逻辑。另一个原因是load
方法是线程安全的,它内部使用了锁,所以我们应该避免线程阻塞在load
方法中。
一个常见的使用场景是在load
方法中实现Method Swizzle:
// In Other.m
+ (void)load {Method originalFunc = class_getInstanceMethod([self class], @selector(originalFunc));Method swizzledFunc = class_getInstanceMethod([self class], @selector(swizzledFunc));method_exchangeImplementations(originalFunc, swizzledFunc);
}
在Child
类的load
方法中,由于还没调用Other
的load
方法,所以输出结果是"Original Output",而在main函数中,输出结果自然就变成了"Swizzled Output"。
一般来说,除了Method Swizzle,别的逻辑都不应该放在load
方法中实现。
runtime中的源码
load调用方式
+load是通过函数指针指向函数,拿到函数地址,分开来直接调用的,直接通过内存地址查找调用的。
initialize
这个方法在第一次给某个类发送消息时调用(比如实例化一个对象),并且只会调用一次。initialize
方法实际上是一种惰性调用,也就是说如果一个类一直没被用到,那它的initialize
方法也不会被调用,这一点有利于节约资源。
调用规则
与load
方法类似的是,在initialize
方法内部也会调用父类的方法,而且不需要我们显示的写出来。与load
方法不同之处在于,即使子类没有实现initialize
方法,也会调用父类的方法,这会导致一个很严重的问题:
// In Parent.m
+ (void)initialize {NSLog(@"Initialize Parent, caller Class %@", [self class]);
}// In Child.m
// 注释掉initialize方法// In main.m
Child *child = [Child new];
运行后发现父类的initialize
方法竟然调用了两次:
2016-02-01 22:57:02.985 load[12772:1509345] Initialize Parent, caller Class Parent
2016-02-01 22:57:02.985 load[12772:1509345] Initialize Parent, caller Class Child
这是因为在创建子类对象时,首先会调用一次父类的initialize
方法,然后创建子类时,尽管child自己没有实现initialize
方法,但是通过obj_msgsend调用,会找到父类的方法,然后又调用了一次。
虽然initialize
方法对一个类而言只会调用一次,但这里由于出现了两个类,所以调用两次符合规则,但不符合我们的需求。正确使用initialize
方法的姿势如下(官方文档上这么说的):
// In Parent.m
+ (void)initialize {if (self == [Parent class]) {NSLog(@"Initialize Parent, caller Class %@", [self class]);}
}
加上判断后,就不会因为子类而调用到自己的initialize
方法了。
最后结论(参考链接 https://www.jianshu.com/p/c52d0b6ee5e9) :
- 1.父类的initialize方法会比子类先执行
- 2.当子类未实现initialize方法时,会调用父类initialize方法,子类实现initialize方法时,会覆盖父类initialize方法.
- 3.当有多个Category都实现了initialize方法,会覆盖类中的方法,只执行一个(会执行Compile Sources 列表中最后一个Category 的initialize方法)
使用场景
initialize
方法主要用来对一些不方便在编译期初始化的对象进行赋值。比如NSMutableArray
这种类型的实例化依赖于runtime的消息发送,所以显然无法在编译器初始化:
// In Parent.m
static int someNumber = 0; // int类型可以在编译期赋值
static NSMutableArray *someObjects;+ (void)initialize {if (self == [Parent class]) {// 不方便编译期复制的对象在这里赋值someObjects = [[NSMutableArray alloc] init];}
}
initialize调用方式
在类第一次接收到消息时调用,消息转发机制调用的
void _class_initialize(Class cls)
{... ...// 递归调用父类的_class_initialize方法supercls = cls->superclass;if (supercls && !supercls->isInitialized()) {_class_initialize(supercls);}// 将要调用SEL_initializeif (PrintInitializing) {_objc_inform("INITIALIZE: calling +[%s initialize]",cls->nameForLogging());}// 通过消息机制调用SEL_initialize((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);// 调用完成if (PrintInitializing) {_objc_inform("INITIALIZE: finished +[%s initialize]",cls->nameForLogging());}
总结
load
和initialize
方法都会在实例化对象之前调用,在runtime初始化完成后调用,后者在收到第一条消息前调用。这两个方法会被自动调用,不能手动调用它们 , 不要写 [super load] / [superinitialize
]。load
和initialize
方法都不用显示的调用父类的方法而是自动调用,即使子类没有initialize
方法也会调用父类的方法,而load
方法则不会调用父类。load
方法通常用来进行Method Swizzle,initialize
方法一般用于初始化全局变量或静态变量。load
和initialize
方法内部使用了锁,因此它们是线程安全的。实现时要尽可能保持简单,避免阻塞线程,不要再使用锁。+load调用方式:+load是通过函数指针指向函数,拿到函数地址,分开来直接调用的,直接通过内存地址查找调用的。
+initialize:在类第一次接收到消息时调用,消息转发机制调用的(objc_msgSend).
+initialize方法调用顺序, 所以类别中的initialize会覆盖主类的initialize.
如果子类没有实现+initialize方法,会调用父类的+initialize(所以父类的+initialize方法可能会被调用多次)
原文链接:http://www.jianshu.com/p/d25f691f0b07
+(void)load | +(void)initialize | |
执行时机 | 在程序运行后立即执行,runtime初始化中,main函数前 | 在类的方法第一次被调时执行 |
若自身未定义,是否沿用父类的方法? | 否 | 是 |
类别中的定义 | 全都执行,但后于类中的方法 | 覆盖类中的方法,只执行一个 |
OC load 和 initialize 方法相关推荐
- oc---类方法load和initialize的区别
在iOS开发中,就像Application有生命周期回调方法一样,在Objective-C的类被加载和初始化的时候,也可以收到方法回调,可以在适当的情况下做一些定制处理.而这正是本篇文章所要介绍的lo ...
- 解读OC中的load和initialize
在 Objective-C 中,NSObject 是绝大多数类的基类.而在 NSObject 中有两个类方法 load 和 initialize,那这两个方法是在什么时机被调用呢?父类.Categor ...
- 你需要了解的load和initialize
NSObject类有两种初始化方式load和initialize load + (void)load; 复制代码 对于加入运行期系统的类及分类,必定会调用此方法,且仅调用一次. iOS会在应用程序启动 ...
- Load和Initialize往死了问是一种怎样的体验?
2017.08.08 这个问题有点炒冷饭了,写了 demo 才更深入的了解这个问题,就当做个简单的笔记吧 一. Load和Initialize的往死了问是一种怎样的体验? Load 和 Initial ...
- iOS---Objective-C: +load vs +initialize
在 NSObject 类中有两个非常特殊的类方法 +load 和 +initialize ,用于类的初始化.这两个看似非常简单的类方法在许多方面会让人感到困惑,比如: 子类.父类.分类中的相应方法什么 ...
- Load和Initialize的往死了问是一种怎样的体验
Load 和 Initialize 先加载哪个? 父类和子类以及 Category 的关系? 如果是多个 Category 呢? Load 开发文档的直接解读 加载顺序总结 所有类和分类的 +load ...
- 在c语言中load,一道题理清Objective-C中的load和initialize
Objective-C中有两个方法比较特殊,他们会在Runtime时根据情况自动调用,下面我们简单分析一下调用时机以及使用场景~ 一般在iOS初中级面试时偶尔会被问到load和initialize方法 ...
- iOS之深入解析类方法+load与+initialize的底层原理
一.前言 Objective-C 作为一门面向对象语言,有类和对象的概念.编译后,类相关的数据结构会保留在目标文件中,在运行时得到解析和使用.在应用程序运行起来的时候,类的信息会有加载和初始化过程. ...
- iOS load方法和initialize方法的异同
对于OC中的类来说,在runtime中会有两个方法被调用: +load +initialize 这两个方法看起来都是在类初始的时候调用的,但其实还是有一些异同,从而可以用来做一些行为. +load 首 ...
- initialize方法与load方法比较
load方法和initialize方法类似点 1. 都只会调用一次2. 父类在子类之前加载 复制代码 不同点在于: 1. 加载时间不同,load方法在main()函数前进行调用,initialize在 ...
最新文章
- TIOBE 2月编程语言排行榜:Python逼近C,Groovy重回TOP 20
- Moses 里的参数(未完成)
- java 理解break,continue,return
- owls-uddi-matchmaker安装
- NetCore 依赖注入之服务之间的依赖关系
- leetcode114. 二叉树展开为链表
- 史上最全的延迟任务实现方式汇总!附代码(强烈推荐)
- 深入Java虚拟机-第二章-Java内存区域-学习笔记
- 赢在电子商务网站制作与营销:B2C版
- 浅析pc机上如何将vmlinuz-2.6.31-14-generic解压出vmlinux
- 使用ImageMagick如何对图片进行全面压缩
- Matlab isnan isinf median circshift 函数
- 颜色编码 RGB、CMYK、 HEX
- 乐队设备--功放的学习和使用
- 达梦数据库下载【最新版本】
- 一个初级运维工程师对于运维工作的一些浅显认知
- 走进web(关于web、web1.0、web2.0、web3.0)
- Java写一个excel工具类_Java中Excel写操作工具类--Java免费学习网
- ipref网络性能评估工具
- 【MySQL主从复制】使用MySQL8.0.17的clone技术在线搭建主从复制环境