OC中有两个特殊的类方法,分别是loadinitialize

先来看看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:

  1. All initializers in any framework you link to. // 调用所有的Framework中的初始化方法
  2. All +load methods in your image. // 调用所有的+load方法
  3. All C++ static initializers and C/C++ __attribute__(constructor) functions in your image. // 调用C++的静态初始化方及C/C++中的attribute(constructor)函数
  4. All initializers in frameworks that link to you. // 调用所有链接到目标文件的framework中的初始化方法

In addition:

  • A class’s+loadmethod is called after all of its superclasses’+loadmethods.
  • A category+loadmethod is called after the class’s own+loadmethod.
  • 先父类load-> 在子类load->最后category中load(父类的category优先级也低于子类)

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方法可能尚未被调用。

+(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的输出结果中,Parentload方法先于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方法中,由于还没调用Otherload方法,所以输出结果是"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());}        

总结

  1. loadinitialize方法都会在实例化对象之前调用,在runtime初始化完成后调用,后者在收到第一条消息前调用。这两个方法会被自动调用,不能手动调用它们 , 不要写 [super load] / [super initialize]。
  2. loadinitialize方法都不用显示的调用父类的方法而是自动调用,即使子类没有initialize方法也会调用父类的方法,而load方法则不会调用父类。
  3. load方法通常用来进行Method Swizzle,initialize方法一般用于初始化全局变量或静态变量。
  4. loadinitialize方法内部使用了锁,因此它们是线程安全的。实现时要尽可能保持简单,避免阻塞线程,不要再使用锁。
  5. +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 方法相关推荐

  1. oc---类方法load和initialize的区别

    在iOS开发中,就像Application有生命周期回调方法一样,在Objective-C的类被加载和初始化的时候,也可以收到方法回调,可以在适当的情况下做一些定制处理.而这正是本篇文章所要介绍的lo ...

  2. 解读OC中的load和initialize

    在 Objective-C 中,NSObject 是绝大多数类的基类.而在 NSObject 中有两个类方法 load 和 initialize,那这两个方法是在什么时机被调用呢?父类.Categor ...

  3. 你需要了解的load和initialize

    NSObject类有两种初始化方式load和initialize load + (void)load; 复制代码 对于加入运行期系统的类及分类,必定会调用此方法,且仅调用一次. iOS会在应用程序启动 ...

  4. Load和Initialize往死了问是一种怎样的体验?

    2017.08.08 这个问题有点炒冷饭了,写了 demo 才更深入的了解这个问题,就当做个简单的笔记吧 一. Load和Initialize的往死了问是一种怎样的体验? Load 和 Initial ...

  5. iOS---Objective-C: +load vs +initialize

    在 NSObject 类中有两个非常特殊的类方法 +load 和 +initialize ,用于类的初始化.这两个看似非常简单的类方法在许多方面会让人感到困惑,比如: 子类.父类.分类中的相应方法什么 ...

  6. Load和Initialize的往死了问是一种怎样的体验

    Load 和 Initialize 先加载哪个? 父类和子类以及 Category 的关系? 如果是多个 Category 呢? Load 开发文档的直接解读 加载顺序总结 所有类和分类的 +load ...

  7. 在c语言中load,一道题理清Objective-C中的load和initialize

    Objective-C中有两个方法比较特殊,他们会在Runtime时根据情况自动调用,下面我们简单分析一下调用时机以及使用场景~ 一般在iOS初中级面试时偶尔会被问到load和initialize方法 ...

  8. iOS之深入解析类方法+load与+initialize的底层原理

    一.前言 Objective-C 作为一门面向对象语言,有类和对象的概念.编译后,类相关的数据结构会保留在目标文件中,在运行时得到解析和使用.在应用程序运行起来的时候,类的信息会有加载和初始化过程. ...

  9. iOS load方法和initialize方法的异同

    对于OC中的类来说,在runtime中会有两个方法被调用: +load +initialize 这两个方法看起来都是在类初始的时候调用的,但其实还是有一些异同,从而可以用来做一些行为. +load 首 ...

  10. initialize方法与load方法比较

    load方法和initialize方法类似点 1. 都只会调用一次2. 父类在子类之前加载 复制代码 不同点在于: 1. 加载时间不同,load方法在main()函数前进行调用,initialize在 ...

最新文章

  1. TIOBE 2月编程语言排行榜:Python逼近C,Groovy重回TOP 20
  2. Moses 里的参数(未完成)
  3. java 理解break,continue,return
  4. owls-uddi-matchmaker安装
  5. NetCore 依赖注入之服务之间的依赖关系
  6. leetcode114. 二叉树展开为链表
  7. 史上最全的延迟任务实现方式汇总!附代码(强烈推荐)
  8. 深入Java虚拟机-第二章-Java内存区域-学习笔记
  9. 赢在电子商务网站制作与营销:B2C版
  10. 浅析pc机上如何将vmlinuz-2.6.31-14-generic解压出vmlinux
  11. 使用ImageMagick如何对图片进行全面压缩
  12. Matlab isnan isinf median circshift 函数
  13. 颜色编码 RGB、CMYK、 HEX
  14. 乐队设备--功放的学习和使用
  15. 达梦数据库下载【最新版本】
  16. 一个初级运维工程师对于运维工作的一些浅显认知
  17. 走进web(关于web、web1.0、web2.0、web3.0)
  18. Java写一个excel工具类_Java中Excel写操作工具类--Java免费学习网
  19. ipref网络性能评估工具
  20. 【MySQL主从复制】使用MySQL8.0.17的clone技术在线搭建主从复制环境

热门文章

  1. 【工具篇】---2DTileMapLevelEditor地图编辑器
  2. 微软,谷歌和雅虎收购比较
  3. 使用ccproxy代理服务器上网
  4. 【程序人生】我的程序人生,与互联网13年的际遇
  5. 常用数学符号的英文读法
  6. 假币问题python
  7. cisco privilege权限
  8. Python 基于 uiautomator2 实现《全民开喵铺》自动收币,自动签到,自动浏览得喵币
  9. jeecms9自定义标签以及使用新创建的数据库表
  10. java 定义16进制_java数据类型(大小等),变量定义,各进制书写方法