Objective-C类初始化:load与initialize
来源:互联网 发布:aspen8.4软件下载 编辑:程序博客网 时间:2024/06/05 19:00
在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 loadingConstructorLoadmain 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 StartAbstractBase initializeSubClasss initializemain 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方法可以帮我们做到的。
+ (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函数实现如下:
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的原因。再看下面这段:
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方法实现,如下:
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/1630979http://wufawei.com/2013/06/load-initialize/
http://www.cocoachina.com/ios/20150104/10826.html
- Objective-C类初始化:load与initialize
- Objective-C 中 +load 与 +initialize
- Objective-C 中 +load 与 +initialize
- Objective-C 中 +load 与 +initialize
- Objective-C 中 +load 与 +initialize
- Objective-C 中 +load 与 +initialize
- Objective-C 中 +load 与 +initialize
- Objective-C 中 +load 与 +initialize
- Objective-C 中 +load 与 +initialize
- Objective-C类方法load和initialize
- Objective C类方法load和initialize的区别
- Objective C类方法load和initialize的区别
- Objective C类方法load和initialize的区别
- Objective C类方法load和initialize的区别
- Objective C类方法load和initialize的区别
- 1.Objective C类方法load和initialize的区别
- Objective C类方法load和initialize的区别
- Objective C类方法load和initialize的区别
- obj-y +=和obj-y :=的区别
- UVALive 6934 Good morning! 打表判断
- 【JS/读书随笔】JavaScript编程精解/Eloquent JavaScript:Chapter 2 函数
- 二叉树的先序、中序、后序的递归及非递归实现,以及层次遍历的实现:
- 123
- Objective-C类初始化:load与initialize
- 浙江大学PAT_甲级_1071. Speech Patterns (25)
- iOS异常、错误汇总(一)
- Qt下载网址
- android--笔记--自定义view时的命名空间引用改为res-auto
- lintcode-384
- hdu 4323 编辑距离
- 稳定匹配问题
- eclipse MAT - Memory Analyzer Tool 使用进阶