一. 版本1
在Objective-C中,NSObject是根类,而NSObject.h的头文件中前两个方法就是load和initialize两个类方法,本篇文章就对这两个方法做下说明和整理。Objective-C作为一门面向对象语言,有类和对象的概念。编译后,类相关的数据结构会保留在目标文件中,在运行时得到解析和使用。在应用程序运行起来的时候,类的信息会有加载和初始化过程。
就像Application有生命周期回调方法一样,在Objective-C的类被加载和初始化的时候,也可以收到方法回调,可以在适当的情况下做一些定制处理。而这正是load和initialize方法可以帮我们做到的。
1
2
|
+ (void)load;
+ (void)initialize;
|
1. load和initialize的共同特点
在不考虑开发者主动使用的情况下,系统最多会调用一次
如果父类和子类都被调用,父类的调用一定在子类之前
都是为了应用运行提前创建合适的运行环境
在使用时都不要过重地依赖于这两个方法,除非真正必要
2. load方法相关要点
调用时机比较早,运行环境有不确定因素。具体说来,在iOS上通常就是App启动时进行加载,但当load调用的时候,并不能保证所有类都加载完成且可用,必要时还要自己负责做auto release处理。
对于有依赖关系的两个库中,被依赖的类的load会优先调用。但在一个库之内,调用顺序是不确定的。
对于一个类而言,没有load方法实现就不会调用,不会考虑对NSObject的继承。
一个类的load方法不用写明[super load],父类就会收到调用,并且在子类之前。
Category的load也会收到调用,但顺序上在主类的load调用之后。
不会直接触发initialize的调用。
3. initialize方法相关要点
initialize的自然调用是在第一次主动使用当前类的时候(lazy,这一点和Java类的“clinit”的很像)。
在initialize方法收到调用时,运行环境基本健全。
initialize的运行过程中是能保证线程安全的。
和load不同,即使子类不实现initialize方法,会把父类的实现继承过来调用一遍。注意的是在此之前,父类的方法已经被执行过一次了,同样不需要super调用。
4.[ A alloc];
alloc将为A类实例在堆上分配变量。这时调用一次initialize方法,而且仅调用一次,也就是说再次alloc操作的时候,不会再调用initialize方法了。
initialize 会在运行时仅被触发一次,如果没有向类发送消息的话,这个方法将不会被调用。这个方法的调用是线程安全的。父类会比子类先收到此消息。
如果希望在类及其Categorgy中执行不同的初始化的话可以使用
+(void)load; 在Objective-C运行时载入类或者Category时被调用
这个方法对动态库和静态库中的类或(Category)都有效.
在Mac OS X 10.5及之后的版本,初始化的顺序如下:
1. 调用所有的Framework中的初始化方法
2. 调用所有的+load方法
3. 调用C++的静态初始化方及C/C++中的__attribute__(constructor)函数
4. 调用所有链接到目标文件的framework中的初始化方法
另外
* 一个类的+load方法在其父类的+load方法后调用
* 一个Category的+load方法在被其扩展的类的自有+load方法后调用
在+load方法中,可以安全地向同一二进制包中的其它无关的类发送消息,但接收消息的类中的+load方法可能尚未被调用。
5.为什么没有执行类中的initialize而是执行Category中的initialize方法??(Category覆盖方法时优先级更高)
二.版本2
initialize和load的区别在于:load是只要类所在文件被引用就会被调用,而initialize是在类或者其子类的第一个方法被调用前调用。所以如果类没有被引用进项目,就不会有load调用;但即使类文件被引用进来,但是没有使用,那么initialize也不会被调用。
+ (void)initialize
消息是在该类接收到其第一个消息之前调用。关于这里的第一个消息需要特别说明一下,对于 NSObject
的 runtime
机制而言,其在调用 NSObject
的 + (void)load
消息不被视为第一个消息,但是,如果像普通函数调用一样直接调用 NSObject
的 + (void)load
消息,则会引起 + (void)initialize
的调用。反之,如果没有向 NSObject
发送第一个消息,+ (void)initialize
则不会被自动调用。runtime
只会向每个类发送一次 + (void)initialize
消息,如果该类是子类,且该子类中没有实现 + (void)initialize
消息,或者子类显示调用父类实现 [super initialize]
, 那么则会调用其父类的实现。也就是说,父类的 + (void)initialize
可能会被调用多次。initialize
方法,那么则会调用分类的 initialize
实现,而原类的该方法实现不会被调用,这个机制同 NSObject
的其他方法(除 + (void)load
方法) 一样,即如果原类同该类的分类包含有相同的方法实现,那么原类的该方法被隐藏而无法被调用。initialize
方法先于子类的 initialize
方法调用。如果你没有用到你重写的这个控制器.那你重写的load类方法也会调用.换句话说,这个load方法是在didFinishLaunchingWithOptions方法之前就被调用了.
用法:load类方法,歌没有用过.只是用过initialize类方法.所以....
initialize类方法的一个金典应用:
往往在一个应用APP中,导航条控制器的导航条几乎是统一的.再加上,一般情况一个应用APP会重写一个导航控制器类.那么initialize类方法就能出场了.比如:
+ (void)initialize {
NSLog(@"%s:%s",__FILE__,__func__);
//设置导航栏主题
UINavigationBar *navBar = [UINavigationBar appearance];
//接下来就可以对navBar坐各种统一的设置处理了.比如字体,背景....
}
三.版本3
当父类和子类都实现load函数时,父类的load函数会被先执行。load函数是系统自动加载的,因此不需要调用父类的load函数,否则父类的load函数会多次执行。
在Category中写load函数是不会替换原始类中的load函数的,原始类和Category中的load函数都会被执行,原始类的load会先被执行,再执行Category中的load函数。当有多个Category都实现了load函数,这几个load函数执行顺序不确定。
如果类包含继承关系,父类的initialize函数会比子类先执行。由于是系统自动调用,也不需要显式的调用父类的initialize,否则父类的initialize会被多次执行。
假如这个类放到代码中,而这段代码并没有被执行,这个函数是不会被执行的。
这两个函数没有交集,也没有执行的先后顺序,他们各自遵循着各自的调用原则。因此在写逻辑的时候,不能有逻辑依赖load函数比initialize函数先行调用。
将针对于类修改放在intialize中,将针对Category的修改放在load中。
但是假如我们是修改系统的类,一般会通过添加Category来添加功能,但是如果修改initialize会导致原生的intialize不会执行,所以放在load中会比较妥当。
load
方法在这个文件被程序装载时调用。只要是在Compile Sources中出现的文件总是会被装载,这与这个类是否被用到无关,因此load
方法总是在main
函数之前调用。
如果一个类实现了load
方法,在调用这个方法前会首先调用父类的load
方法。而且这个过程是自动完成的,并不需要我们手动实现:
如果一个类没有实现load
方法,那么就不会调用它父类的load
方法,这一点与正常的类继承和方法调用不一样,需要额外注意一下。
load
方法调用时,系统处于脆弱状态,如果调用别的类的方法,且该方法依赖于那个类的load
方法进行初始化设置,那么必须确保那个类的load
方法已经调用了
load
和initialize
方法都会在实例化对象之前调用,以main函数为分水岭,前者在main函数之前调用,后者在之后调用。这两个方法会被自动调用,不能手动调用它们。load
和initialize
方法都不用显示的调用父类的方法而是自动调用,即使子类没有initialize
方法也会调用父类的方法,而load
方法则不会调用父类。load
方法通常用来进行Method Swizzle,initialize
方法一般用于初始化全局变量或静态变量。load
和initialize
方法内部使用了锁,因此它们是线程安全的。实现时要尽可能保持简单,避免阻塞线程,不要再使用锁。