本文主要是介绍Objective-C类初始化:load与initialize,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
在C语言中,main()是程序最早开始启动和初始化的地方。 在Objective-C中,application,documents,user interface都有自己初始化的地方,所以,在main()中只是简单的调用 NSApplicationMain。
Objective-C中第一个初始化的地方是 +(void)load. 任何class都能有一个+(void)load。
1 +load
+load方法在类被加载到系统时调用,这发生在一个很早的时刻, +load的方法的调用顺序为:
- 链接到的框架初始化(包括+load)
- 当前镜像(image)的+load
- 当前镜像的C++静态初始化和C/C++ attribute(constructor)构造函数
- 链接到你的框架初始化。
下面可以看到load被加载的时机:
+load方法调用的时候尚未有自动释放池,所以如果+load中的内容使用到了自动释放池,则代码需要用@autoreleasepool{}包含。 另外,对应Category中的+load方法并不会覆盖类自身中的+load方法,两个+load方法都会调用,而且Category的+load方法在类自身的+load方法之后调用。
代码例子:
@implementation LoadClass+ (void)load
{NSLog(@"####loading");
}__attribute__((constructor)) staticvoid ConstructorLoad(void)
{NSLog(@"ConstructorLoad ");
}@end@implementation LoadClass (LoadClass_category)+ (void)load
{NSLog(@"####LoadClass_category loading ");
}@endint main(int argc, char *argv[])
{@autoreleasepool {@autoreleasepool {NSLog(@"main Start");}returnUIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegateclass]));}
}
输出
####loading
####LoadClass_category loading
ConstructorLoad
main Start
2 +initialize
+initialize方法在一个比较安全的环境中调用,比+load更适合放入初始化代码。+initialize方法在类首次收到消息时调用,若此类一直没有使用,则+initialize就不会调用。 与+load方法类似的时,在向子类发送initialize消息前,总是会先向父类发送initialize消息,如果父类尚未调用+initialize方法则调用
Category中的+initialize方法会覆盖类本身的+initialize方法。如果父类中实现了+initialize方法,而子类中没有重写此方法,则子类初始化时就会使用父类的方法。
代码:
#import@interface AbstractBase : NSObject
@end@implementation AbstractBase+ (void)initialize
{
NSLog(@"%@ initialize in ",self);
}
@end@interface SubClasss : AbstractBase
@end@implementation SubClasss
@endint main()
{
@autoreleasepool {
NSLog(@"main Start");
[SubClasss class];
NSLog(@"main End");}
return0;
}**其输出为:**main Start
AbstractBase initialize
SubClasss initialize
main End
如果不想子类使用父类的+initialize方法,则父类的+initialize方法可以这样写
+ (void)initialize
{if(self == [ParentClass class]){... init..}
}
1) The App Launch Sequence on iOS http://oleb.net/blog/2011/06/app-launch-sequence-ios/
2) http://www.friday.com/bbum/2009/09/06/iniailize-can-be-executed-multiple-times-load-not-so-much/
3) Objective-C类初始化:load与initialize
http://www.winddisk.com/2012/08/19/objective-c-class-load-initialize/
4) Friday Q&A 2009-05-22: Objective-C Class Loading and Initializationhttp://www.mikeash.com/pyblog/friday-qa-2009-05-22-objective-c-class-loading-and-initialization.html
类的加载
在java语言里,可以通过如下代码来实现加载类的时候执行对类的操作,一般叫:类初始块,或者,类加载块。比如:
- public class MyClass{
- static{
- ……
- }
- }
在objc语言里,对应的机制是,2个类初始化方法,+(void)load和+(void)initialize。
比如:
- #import "Constants.h"
- @implementation Constants
- + (void)initialize{
- NSLog(@"init constants >>>>>>>>>>");
- }
- + (void)load{
- NSLog(@"load constants >>>>>>>>>>");
- }
- @end
两个方法有一些不同。
load,是加载类的时候,这里是Constants类,就会调用。也就是说,ios应用启动的时候,就会加载所有的类,就会调用这个方法。
这样有个缺点,当加载类需要很昂贵的资源,或者比较耗时的时候,可能造成不良的用户体验,或者系统的抖动。这时候,就要考虑initialize方法了。这个方法可看作类加载的延时加载方法。类加载后并不执行该方法。只有当实例化该类的实例的时候,才会在第一个实例加载前执行该方法。比如:
[Constants alloc];
alloc将为Constants实例在堆上分配变量。这时调用一次initialize方法,而且仅调用一次,也就是说再次alloc操作的时候,不会再调用initialize方法了。
initialize 会在运行时仅被触发一次,如果没有向类发送消息的话,这个方法将不会被调用。这个方法的调用是线程安全的。父类会比子类先收到此消息。
如果希望在类及其Categorgy中执行不同的初始化的话可以使用+load
+(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方法可能尚未被调用。
下面是一个load的顺序
- #import <Foundation/Foundation.h>
- #define LOAD +(void)load{NSLog(@"%s", __PRETTY_FUNCTION__);}
- #define INITIALIZE +(void)initialize{NSLog(@"%s", __PRETTY_FUNCTION__);}
- #define DEF_CLASS(clsName) @interface clsName : NSObject \
- @end \
- @implementation clsName \
- INITIALIZE \
- LOAD \
- @end
- DEF_CLASS(C1) //1
- DEF_CLASS(C2) //2
- @interface C1 (Hello)
- + (void)hello;
- @end
- @implementation C1 (Hello)
- INITIALIZE //
- LOAD //3
- + (void)hello
- {
- NSLog(@"Hello");
- }
- + (void)hi
- {
- NSLog(@"hi");
- }
- @end
- @interface C2 (Hello)
- @end
- @implementation C2 (Hello)
- + (void)load //4
- {
- NSLog(@"%s", __PRETTY_FUNCTION__);
- }
- + (void)initialize //
- {
- NSLog(@"%s", __PRETTY_FUNCTION__);
- }
- @end
- int main (int argc, const char * argv[])
- {
- @autoreleasepool {
- // insert code here...
- // [[C1 alloc]init]; // 向C1发送消息
- // [[C2 alloc]init]; // 向C2发送消息
- }
- return 0;
- }
输出:
2012-08-1400:47:07.859 www[654:903] +[C1 load]
2012-08-1400:47:07.862 www[654:903] +[C2 load]
2012-08-14 00:47:07.863 www[654:903] +[C1(Hello) load]
2012-08-14 00:47:07.863 www[654:903] +[C2(Hello) load]
以上只执行了load方法。
load和initialize的顺序:
- #import <Foundation/Foundation.h>
- #define LOAD +(void)load{NSLog(@"%s", __PRETTY_FUNCTION__);}
- #define INITIALIZE +(void)initialize{NSLog(@"%s", __PRETTY_FUNCTION__);}
- #define DEF_CLASS(clsName) @interface clsName : NSObject \
- @end \
- @implementation clsName \
- INITIALIZE \
- LOAD \
- @end
- DEF_CLASS(C1) //1
- DEF_CLASS(C2) //2
- @interface C1 (Hello)
- + (void)hello;
- @end
- @implementation C1 (Hello)
- INITIALIZE //4
- LOAD //3
- + (void)hello
- {
- NSLog(@"Hello");
- }
- + (void)hi
- {
- NSLog(@"hi");
- }
- @end
- @interface C2 (Hello)
- @end
- @implementation C2 (Hello)
- + (void)load //5
- {
- NSLog(@"%s", __PRETTY_FUNCTION__);//[C1 hello];//NSLog(@"%s", __PRETTY_FUNCTION__);
- }
- + (void)initialize //6
- {
- NSLog(@"%s", __PRETTY_FUNCTION__);
- }
- @end
- int main (int argc, const char * argv[])
- {
- @autoreleasepool {
- // insert code here...
- [[C1 alloc]init]; // 向C1发送消息
- [[C2 alloc]init]; // 向C2发送消息
- }
- return 0;
- }
输出:
2012-08-1400:55:26.769 www[741:903] +[C1 load]
2012-08-1400:55:26.772 www[741:903] +[C2 load]
2012-08-14 00:55:26.772 www[741:903] +[C1(Hello) load]
2012-08-14 00:55:26.773 www[741:903] +[C2(Hello) load]
2012-08-14 00:55:26.773 www[741:903] +[C1(Hello) initialize]
2012-08-14 00:55:26.774 www[741:903] +[C2(Hello) initialize]
貌似类中的initialize没有执行。alloc]init某个类就调用每个类的initialize方法。
假如只[[C2 alloc]init];就只执行C2类中的initialize方法 ,输出:
2012-08-1400:55:26.769 www[741:903] +[C1 load]
2012-08-1400:55:26.772 www[741:903] +[C2 load]
2012-08-14 00:55:26.772 www[741:903] +[C1(Hello) load]
2012-08-14 00:55:26.773 www[741:903] +[C2(Hello) load]
2012-08-14 00:55:26.774 www[741:903] +[C2(Hello) initialize]
再看下面代码:
- #import <Foundation/Foundation.h>
- #define LOAD +(void)load{NSLog(@"%s", __PRETTY_FUNCTION__);}
- #define INITIALIZE +(void)initialize{NSLog(@"%s", __PRETTY_FUNCTION__);}
- #define DEF_CLASS(clsName) @interface clsName : NSObject \
- @end \
- @implementation clsName \
- INITIALIZE \
- LOAD \
- @end
- DEF_CLASS(C1) //1
- DEF_CLASS(C2) //2
- @interface C1 (Hello)
- + (void)hello;
- @end
- @implementation C1 (Hello)
- INITIALIZE //4
- LOAD //3
- + (void)hello
- {
- NSLog(@"Hello");
- }
- + (void)hi
- {
- NSLog(@"hi");
- }
- @end
- @interface C2 (Hello)
- @end
- @implementation C2 (Hello)
- + (void)load //5
- {
- [C1 hello];//NSLog(@"%s", __PRETTY_FUNCTION__); //这里做了修改,调用了C1的静态方法,导致执行了C1的+initialize
- }
- + (void)initialize //6
- {
- NSLog(@"%s", __PRETTY_FUNCTION__);
- }
- @end
- int main (int argc, const char * argv[])
- {
- @autoreleasepool {
- // insert code here...
- // [[C1 alloc]init]; // 向C1发送消息
- [[C2 alloc]init]; // 向C2发送消息
- }
- return 0;
- }
输出:
2012-08-1423:31:28.834 www[14975:903] +[C1 load]
2012-08-1423:31:28.837 www[14975:903] +[C2 load]
2012-08-14 23:31:28.837 www[14975:903] +[C1(Hello) load]
2012-08-14 23:31:28.838 www[14975:903] +[C1(Hello) initialize]
2012-08-1423:31:28.838 www[14975:903] Hello
2012-08-14 23:31:28.839 www[14975:903] +[C2(Hello) initialize]
问题:
1,在倒数第二个代码中,为什么没有执行类中的initialize而是执行Category中的initialize方法??(Category覆盖方法时优先级更高)
要点:
1、initialize和load,我们并不需要在这两个方法的实现中使用super调用父类的方法。
2、load和initialize被调用一次是相对runtime而言 ,你可以当作普通类方法多次调用。
3、类加载到系统的时候就用调用load方法,类首次使用的时候调用initialize方法。
4、load不像普通方法一样遵从那套继承规则,当每个类没有实现 load方法,不管各级超类是否实现,系统都不会调用此类的load方法。initialize与其他方法一样,如果每个类没有实现initialize方法,而超类实现了,那么就会执行超类的这个方法,所以通常会:
- +(void)initialize{
- if(self == [XXXClass clasee])
- //todo
- }
5、initialize和load的方法必须写的精简。
6、initialize中可以实现无法在编译期初始化的全局变量,load的方法中可以实现swizzling的逻辑。
7、load的调用并不视为类的第一个方法完成,因为load中调用了当前类中的方法,就先去执行initialize方法了。
8、Runtime调用+(void)load时没有autorelease pool,
- @interface MainClass : NSObject
- @end
- @implementation MainClass
- + (void) load {
- NSArray *array = [NSArray array];
- NSLog(@"%@ %s", array, __FUNCTION__);
- }
- @end
其原因是runtime调用+(void)load的时候,程序还没有建立其autorelease pool,所以那些会需要使用到autorelease pool的代码,都会出现异常。这一点是非常需要注意的,也就是说放在+(void)load中的对象都应该是alloc出来并且不能使用autorelease来释放。
9、load方法调用的顺序:父类(Superclass)的方法优先于子类(Subclass)的方法,类中的方法优先于类别(Category)中的方法。
10、所有类别(Category)中的load方法都会执行。
11、最后一个类别(Category)中的initialize方法会覆盖之前类别和类中的initialize方法。
类的加载
在java语言里,可以通过如下代码来实现加载类的时候执行对类的操作,一般叫:类初始块,或者,类加载块。比如:
- public class MyClass{
- static{
- ……
- }
- }
在objc语言里,对应的机制是,2个类初始化方法,+(void)load和+(void)initialize。
比如:
- #import "Constants.h"
- @implementation Constants
- + (void)initialize{
- NSLog(@"init constants >>>>>>>>>>");
- }
- + (void)load{
- NSLog(@"load constants >>>>>>>>>>");
- }
- @end
两个方法有一些不同。
load,是加载类的时候,这里是Constants类,就会调用。也就是说,ios应用启动的时候,就会加载所有的类,就会调用这个方法。
这样有个缺点,当加载类需要很昂贵的资源,或者比较耗时的时候,可能造成不良的用户体验,或者系统的抖动。这时候,就要考虑initialize方法了。这个方法可看作类加载的延时加载方法。类加载后并不执行该方法。只有当实例化该类的实例的时候,才会在第一个实例加载前执行该方法。比如:
[Constants alloc];
alloc将为Constants实例在堆上分配变量。这时调用一次initialize方法,而且仅调用一次,也就是说再次alloc操作的时候,不会再调用initialize方法了。
initialize 会在运行时仅被触发一次,如果没有向类发送消息的话,这个方法将不会被调用。这个方法的调用是线程安全的。父类会比子类先收到此消息。
如果希望在类及其Categorgy中执行不同的初始化的话可以使用+load
+(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方法可能尚未被调用。
下面是一个load的顺序
- #import <Foundation/Foundation.h>
- #define LOAD +(void)load{NSLog(@"%s", __PRETTY_FUNCTION__);}
- #define INITIALIZE +(void)initialize{NSLog(@"%s", __PRETTY_FUNCTION__);}
- #define DEF_CLASS(clsName) @interface clsName : NSObject \
- @end \
- @implementation clsName \
- INITIALIZE \
- LOAD \
- @end
- DEF_CLASS(C1) //1
- DEF_CLASS(C2) //2
- @interface C1 (Hello)
- + (void)hello;
- @end
- @implementation C1 (Hello)
- INITIALIZE //
- LOAD //3
- + (void)hello
- {
- NSLog(@"Hello");
- }
- + (void)hi
- {
- NSLog(@"hi");
- }
- @end
- @interface C2 (Hello)
- @end
- @implementation C2 (Hello)
- + (void)load //4
- {
- NSLog(@"%s", __PRETTY_FUNCTION__);
- }
- + (void)initialize //
- {
- NSLog(@"%s", __PRETTY_FUNCTION__);
- }
- @end
- int main (int argc, const char * argv[])
- {
- @autoreleasepool {
- // insert code here...
- // [[C1 alloc]init]; // 向C1发送消息
- // [[C2 alloc]init]; // 向C2发送消息
- }
- return 0;
- }
输出:
2012-08-1400:47:07.859 www[654:903] +[C1 load]
2012-08-1400:47:07.862 www[654:903] +[C2 load]
2012-08-14 00:47:07.863 www[654:903] +[C1(Hello) load]
2012-08-14 00:47:07.863 www[654:903] +[C2(Hello) load]
以上只执行了load方法。
load和initialize的顺序:
- #import <Foundation/Foundation.h>
- #define LOAD +(void)load{NSLog(@"%s", __PRETTY_FUNCTION__);}
- #define INITIALIZE +(void)initialize{NSLog(@"%s", __PRETTY_FUNCTION__);}
- #define DEF_CLASS(clsName) @interface clsName : NSObject \
- @end \
- @implementation clsName \
- INITIALIZE \
- LOAD \
- @end
- DEF_CLASS(C1) //1
- DEF_CLASS(C2) //2
- @interface C1 (Hello)
- + (void)hello;
- @end
- @implementation C1 (Hello)
- INITIALIZE //4
- LOAD //3
- + (void)hello
- {
- NSLog(@"Hello");
- }
- + (void)hi
- {
- NSLog(@"hi");
- }
- @end
- @interface C2 (Hello)
- @end
- @implementation C2 (Hello)
- + (void)load //5
- {
- NSLog(@"%s", __PRETTY_FUNCTION__);//[C1 hello];//NSLog(@"%s", __PRETTY_FUNCTION__);
- }
- + (void)initialize //6
- {
- NSLog(@"%s", __PRETTY_FUNCTION__);
- }
- @end
- int main (int argc, const char * argv[])
- {
- @autoreleasepool {
- // insert code here...
- [[C1 alloc]init]; // 向C1发送消息
- [[C2 alloc]init]; // 向C2发送消息
- }
- return 0;
- }
输出:
2012-08-1400:55:26.769 www[741:903] +[C1 load]
2012-08-1400:55:26.772 www[741:903] +[C2 load]
2012-08-14 00:55:26.772 www[741:903] +[C1(Hello) load]
2012-08-14 00:55:26.773 www[741:903] +[C2(Hello) load]
2012-08-14 00:55:26.773 www[741:903] +[C1(Hello) initialize]
2012-08-14 00:55:26.774 www[741:903] +[C2(Hello) initialize]
貌似类中的initialize没有执行。alloc]init某个类就调用每个类的initialize方法。
假如只[[C2 alloc]init];就只执行C2类中的initialize方法 ,输出:
2012-08-1400:55:26.769 www[741:903] +[C1 load]
2012-08-1400:55:26.772 www[741:903] +[C2 load]
2012-08-14 00:55:26.772 www[741:903] +[C1(Hello) load]
2012-08-14 00:55:26.773 www[741:903] +[C2(Hello) load]
2012-08-14 00:55:26.774 www[741:903] +[C2(Hello) initialize]
再看下面代码:
- #import <Foundation/Foundation.h>
- #define LOAD +(void)load{NSLog(@"%s", __PRETTY_FUNCTION__);}
- #define INITIALIZE +(void)initialize{NSLog(@"%s", __PRETTY_FUNCTION__);}
- #define DEF_CLASS(clsName) @interface clsName : NSObject \
- @end \
- @implementation clsName \
- INITIALIZE \
- LOAD \
- @end
- DEF_CLASS(C1) //1
- DEF_CLASS(C2) //2
- @interface C1 (Hello)
- + (void)hello;
- @end
- @implementation C1 (Hello)
- INITIALIZE //4
- LOAD //3
- + (void)hello
- {
- NSLog(@"Hello");
- }
- + (void)hi
- {
- NSLog(@"hi");
- }
- @end
- @interface C2 (Hello)
- @end
- @implementation C2 (Hello)
- + (void)load //5
- {
- [C1 hello];//NSLog(@"%s", __PRETTY_FUNCTION__); //这里做了修改,调用了C1的静态方法,导致执行了C1的+initialize
- }
- + (void)initialize //6
- {
- NSLog(@"%s", __PRETTY_FUNCTION__);
- }
- @end
- int main (int argc, const char * argv[])
- {
- @autoreleasepool {
- // insert code here...
- // [[C1 alloc]init]; // 向C1发送消息
- [[C2 alloc]init]; // 向C2发送消息
- }
- return 0;
- }
输出:
2012-08-1423:31:28.834 www[14975:903] +[C1 load]
2012-08-1423:31:28.837 www[14975:903] +[C2 load]
2012-08-14 23:31:28.837 www[14975:903] +[C1(Hello) load]
2012-08-14 23:31:28.838 www[14975:903] +[C1(Hello) initialize]
2012-08-1423:31:28.838 www[14975:903] Hello
2012-08-14 23:31:28.839 www[14975:903] +[C2(Hello) initialize]
问题:
1,在倒数第二个代码中,为什么没有执行类中的initialize而是执行Category中的initialize方法??(Category覆盖方法时优先级更高)
要点:
1、initialize和load,我们并不需要在这两个方法的实现中使用super调用父类的方法。
2、load和initialize被调用一次是相对runtime而言 ,你可以当作普通类方法多次调用。
3、类加载到系统的时候就用调用load方法,类首次使用的时候调用initialize方法。
4、load不像普通方法一样遵从那套继承规则,当每个类没有实现 load方法,不管各级超类是否实现,系统都不会调用此类的load方法。initialize与其他方法一样,如果每个类没有实现initialize方法,而超类实现了,那么就会执行超类的这个方法,所以通常会:
- +(void)initialize{
- if(self == [XXXClass clasee])
- //todo
- }
5、initialize和load的方法必须写的精简。
6、initialize中可以实现无法在编译期初始化的全局变量,load的方法中可以实现swizzling的逻辑。
7、load的调用并不视为类的第一个方法完成,因为load中调用了当前类中的方法,就先去执行initialize方法了。
8、Runtime调用+(void)load时没有autorelease pool,
- @interface MainClass : NSObject
- @end
- @implementation MainClass
- + (void) load {
- NSArray *array = [NSArray array];
- NSLog(@"%@ %s", array, __FUNCTION__);
- }
- @end
其原因是runtime调用+(void)load的时候,程序还没有建立其autorelease pool,所以那些会需要使用到autorelease pool的代码,都会出现异常。这一点是非常需要注意的,也就是说放在+(void)load中的对象都应该是alloc出来并且不能使用autorelease来释放。
9、load方法调用的顺序:父类(Superclass)的方法优先于子类(Subclass)的方法,类中的方法优先于类别(Category)中的方法。
10、所有类别(Category)中的load方法都会执行。
11、最后一个类别(Category)中的initialize方法会覆盖之前类别和类中的initialize方法。
概述
Objective-C作为一门面向对象语言,有类和对象的概念。编译后,类相关的数据结构会保留在目标文件中,在运行时得到解析和使用。在应用程序运行起来的时候,类的信息会有加载和初始化过程。
其实在Java语言中也有类似的过程,JVM的ClassLoader也对类进行了加载、连接、初始化。
就像Application有生命周期回调方法一样,在Objective-C的类被加载和初始化的时候,也可以收到方法回调,可以在适当的情况下做一些定制处理。而这正是load和initialize方法可以帮我们做到的。
1 2 | + (void)load; + (void)initialize; |
可以看到这两个方法都是以“+”开头的类方法,返回为空。通常情况下,我们在开发过程中可能不必关注这两个方法。如果有需要定制,我们可以在自定义的NSObject子类中给出这两个方法的实现,这样在类的加载和初始化过程中,自定义的方法可以得到调用。
从如上声明上来看,也许这两个方法和其它的类方法相比没什么特别。但是,这两个方法具有一定的“特殊性”,这也是这两个方法经常会被放在一起特殊提到的原因。详细请看如下几小节的整理。
1. load和initialize的共同特点
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调用。
由于initialize的这些特点,使得其应用比load要略微广泛一些。可用来做一些初始化工作,或者单例模式的一种实现方案。
4. 原理
“源码面前没有秘密”。最后,我们来看看苹果开放出来的部分源码。从中我们也许能明白为什么load和initialize及调用会有如上的一些特点。
其中load是在objc库中一个load_images函数中调用的,先把二进制映像文件中的头信息取出,再解析和读出各个模块中的类定义信息,把实现了load方法的类和Category记录下来,最后统一执行调用。
其中的prepare_load_methods函数实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | void prepare_load_methods(header_info *hi) { Module mods; unsigned int midx; if (_objcHeaderIsReplacement(hi)) { return ; } mods = hi->mod_ptr; for (midx = 0; midx < hi->mod_count; midx += 1) { unsigned int index; if (mods[midx].symtab == nil) continue ; for (index = 0; index < mods[midx].symtab->cls_def_cnt; index += 1) { Class cls = (Class)mods[midx].symtab->defs[index]; if (cls->info & CLS_CONNECTED) { schedule_class_load(cls); } } } mods = hi->mod_ptr; midx = (unsigned int)hi->mod_count; while (midx-- > 0) { unsigned int index; unsigned int total; Symtab symtab = mods[midx].symtab; if (mods[midx].symtab == nil) continue ; total = mods[midx].symtab->cls_def_cnt + mods[midx].symtab->cat_def_cnt; index = total; while (index-- > mods[midx].symtab->cls_def_cnt) { old_category *cat = (old_category *)symtab->defs[index]; add_category_to_loadable_list((Category)cat); } } } |
这大概就是主类中的load方法先于category的原因。再看下面这段:
1 2 3 4 5 6 7 | static void schedule_class_load(Class cls) { if (cls->info & CLS_LOADED) return ; if (cls->superclass) schedule_class_load(cls->superclass); add_class_to_loadable_list(cls); cls->info |= CLS_LOADED; } |
这正是父类load方法优先于子类调用的原因。
再来看下initialize调用相关的源码。objc的库里有一个_class_initialize方法实现,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | void _class_initialize(Class cls) { assert(!cls->isMetaClass()); Class supercls; BOOL reallyInitialize = NO; supercls = cls->superclass; if (supercls && !supercls->isInitialized()) { _class_initialize(supercls); } monitor_enter(&classInitLock); if (!cls->isInitialized() && !cls->isInitializing()) { cls->setInitializing(); reallyInitialize = YES; } monitor_exit(&classInitLock); if (reallyInitialize) { _setThisThreadIsInitializingClass(cls); if (PrintInitializing) { _objc_inform( "INITIALIZE: calling +[%s initialize]" , cls->nameForLogging()); } ((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize); if (PrintInitializing) { _objc_inform( "INITIALIZE: finished +[%s initialize]" , cls->nameForLogging()); } monitor_enter(&classInitLock); if (!supercls || supercls->isInitialized()) { _finishInitializing(cls, supercls); } else { _finishInitializingAfter(cls, supercls); } monitor_exit(&classInitLock); return ; } else if (cls->isInitializing()) { if (_thisThreadIsInitializingClass(cls)) { return ; } else { monitor_enter(&classInitLock); while (!cls->isInitialized()) { monitor_wait(&classInitLock); } monitor_exit(&classInitLock); return ; } } else if (cls->isInitialized()) { return ; } else { _objc_fatal( "thread-safe class init in objc runtime is buggy!" ); } } |
在这段代码里,我们能看到initialize的调用顺序和线程安全性。
http://justsee.iteye.com/blog/1630979 http://wufawei.com/2013/06/load-initialize/
http://www.cocoachina.com/ios/20150104/10826.html
这篇关于Objective-C类初始化:load与initialize的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!