iOS +load 与 +initialize

概述

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

就像Application有生命周期回调方法一样,在Objective-C的类被加载和初始化的时候,也可以收到方法回调,可以在适当的情况下做一些定制处理。而这正是load和initialize方法可以帮我们做到的。

+ load;+ initialize;

可以看到这两个方法都是以“+”开头的类方法,返回为空。通常情况下,我们在开发过程中可能不必关注这两个方法。如果有需要定制,我们可以在自定义的NSObject子类中给出这两个方法的实现,这样在类的加载和初始化过程中,自定义的方法可以得到调用。

属于runtime范畴。

+load

顾名思义,+load方法在这个文件被程序装载时调用。只要是在Compile
Sources中出现的文件总是会被装载,这与这个类是否被用到无关,因此+load方法总是在main函数之前调用。

调用方式:

会循环调用所有类的 +load 方法。注意,这里是(调用分类的 +load
方法也是如此)直接使用函数内存地址的方式 (*load_method)(cls,
SEL_load); 对 +load 方法进行调用的,而不是使用发送消息 objc_msgSend
的方式。

这样的调用方式就使得 +load
方法拥有了一个非常有趣的特性,那就是子类、父类和分类中的 +load
方法的实现是被区别对待的。也就是说如果子类没有实现 +load
方法,那么当它被加载时 runtime 是不会去调用父类的 +load
方法的。同理,当一个类和它的分类都实现了 +load
方法时,两个方法都会被调用。

要点:

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

initialize和load的区别在于:load是只要类所在文件被引用就会被调用,而initialize是在类或者其子类的第一个方法被调用前调用。所以如果类没有被引用进项目,就不会有load调用;但即使类文件被引用进来,但是没有使用,那么initialize也不会被调用。

+initialize

+initialize
方法是在类或它的子类收到第一条消息之前被调用的,这里所指的消息包括实例方法和类方法的调用,并且只会调用一次。initialize方法实际上是一种惰性调用,也就是说如果一个类一直没被用到,那它的initialize方法也不会被调用,这一点有利于节约资源。

调用方式:

runtime 使用了发送消息 objc_msgSend 的方式对 +initialize
方法进行调用。也就是说 +initialize
方法的调用与普通方法的调用是一样的,走的都是发送消息的流程。换言之,如果子类没有实现
+initialize 方法,那么继承自父类的实现会被调用;如果一个类的分类实现了
+initialize 方法,那么就会对这个类中的实现造成覆盖。

要点:

  • initialize的自然调用是在第一次主动使用当前类的时候。
  • 在initialize方法收到调用时,运行环境基本健全。
  • 关于继承:和load不同,即使子类不实现initialize方法,会把父类的实现继承过来调用一遍,就是会沿用父类的+initialize。(沿用父类的方法中,self还是指子类)
  • 父类和本类的调用:子类的+initialize将要调用时会激发父类调用的+initialize方法,所以也不需要在子类写明[super
    initialize]。(本着除主动调用外,只会调用一次的原则,如果父类的+initialize方法调用过了,则不会再调用)
  • 本类和Category的调用:Category中的+initialize方法会覆盖本类的方法,只执行一个Category的+initialize方法。

+(void)load

类别

对于+initialize,只有最后一个类别执行,本类的+initialize和前面类别的+initialize被隐藏。

而对于+load,本类和本类的所有类别都执行,并且如果Apple的文档中介绍顺序一样:先执行类自身的实现,再执行类别中的实现。

执行时机    在程序运行后立即执行

若自身未定义,是否沿用父类的方法?    否

类别中的定义    全都执行,但后于类中的方法

扩展

因为两个方法只会被系统调用一次,并且是线程安全的,可以用来作为单例的实现。(可以用+initialize,+load有些隐患,看这里)

+(void)initialize

�注意

  • 在使用时都不要过重地依赖于这两个方法,除非真正必要。
  • 谨慎在分类中实现+initialize方法,因为如果在分类中实现了,本类实现的+initialize方法将不会被调用。
  • 谨慎在分类中实现+load方法。因为如果在本类中实现+load方法混淆A、B两个方法,分类中也混淆A、B,因为本类和分类的+load都实现了,所以都会调用,A、B在本类中置换后,又在分类中置换了回来。
  • load方法通常用来进行Method
    Swizzle,initialize方法一般用于初始化全局变量或静态变量。
  • load和initialize方法内部使用了锁,因此它们是线程安全的。实现时要尽可能保持简单,避免阻塞线程,不要再使用锁。

执行时机    在类的方法第一次被调时执行

若自身未定义,是否沿用父类的方法?    是

类别中的定义    覆盖类中的方法,只执行一个

问题

问题:

  1. 子类、父类、分类中的相应方法什么时候会被调用?
  2. 需不需要在子类的实现中显式地调用父类的实现?

解答:

    1. super的方法会成功调用,但是这是多余的,因为runtime会自动对父类的+load方法进行调用,而+initialize则会随子类自动激发父类的方法(如Apple文档中所言)不需要显示调用。另一方面,如果父类中的方法用到的self,其指代的依然是类自身,而不是父类。

就像Application有生命周期回调方法一样,在Objective-C的类被加载和初始化的时候,也可以收到方法回调,可以在适当的情况下做一些定制处理。而这正是load和initialize方法可以帮我们做到的。

总结

+load +initialize
调用时机 被添加到 runtime 时 到第一条消息前,可能永远不调用
同一个类,调用次数 1次 1次
调用顺序 父类->本类->分类 父类->本类(如果有分类,则调用分类)
若自身未实现,是否沿用父类的方法?
类别中的定义 全都执行,但后于本类的方法 覆盖本类的方法,只执行一个
线程安全 安全 安全

可以看运行demo查看log

  • Objective-C +load vs +initialize
    文章根据runtime源码来解释+load和+initialize特性。

可以看到这两个方法都是以“+”开头的类方法,返回为空。通常情况下,我们在开发过程中可能不必关注这两个方法。如果有需要定制,我们可以在自定义的NSObject子类中给出这两个方法的实现,这样在类的加载和初始化过程中,自定义的方法可以得到调用。

网站地图xml地图