您的位置:首页 > 其它

初探+load和+initialize方法

2016-08-31 16:45 288 查看
本文引用CaryaLiu的《
iOS初探+load和+initialize
》和sunnyxx的《Notification
Once》的博客



+ initialize
+ load
NSObject
类的两个类方法,它们会在运行时自动调用,我们可以利用其特性做一些初始化操作。

initialize初探

先看看
NSObject Class Reference 中关于
+initialize
的说明:

>

Initializes the class before it receives its first message.

>

The runtime sends initialize to each class in a program just before the class, or any class that inherits from it, is sent its first message from within the program. The runtime sends the initialize message to classes in a thread-safe manner. Superclasses receive
this message before their subclasses. The superclass implementation may be called multiple times if subclasses do not implement initialize—the runtime will call the inherited implementation—or if subclasses explicitly call [super initialize]. If you want to
protect yourself from being run multiple times, you can structure your implementation along these lines:

>

+ (void)initialize {
    if (self == [ClassName self]) {

      // ... do the initialization ...

    }

}

>
initialize
is invoked only once per class. If you want to perform independent initialization for the class and for categories of the class, you should implement
load
methods.

从上面的说明可以看出:

+ (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
方法调用。
示例:

@interface People : NSObject
@end
@implementation People
+ (void)initialize {
   NSLog(@"%s", __FUNCTION__);
}
@end
@interface Student : People
@end
@implementation Student
+ (void)initialize {
   NSLog(@"%s", __FUNCTION__);
}
@end
@implementation ViewController
- (void)viewDidLoad {
   [super viewDidLoad];
   Student *student = [[Student alloc] init];
}
@end

运行程序会输出什么? 答案如下:

  +[People initialize]
  +[Student initialize]

上面示例可以看出子类会调用父类的
+ initialize
方法。

如果
ViewController
内的调用换成如下的方式, 控制台输出又会是什么呢?

@implementation ViewController
- (void)viewDidLoad {
   [super viewDidLoad];
   Student *student = [[Student alloc] init];
   NSLog(@"viewDidLoad finished.");
}
- (void)viewWillAppear:(BOOL)animated {
   [super viewWillAppear:animated];
   Student *student = [[Student alloc] init];
}
@end
输出如下:

  +[People initialize]
  +[Student initialize]
viewDidLoad finished.
上面示例可以看出,对于
runtime
而言,
+ initialize
方法在程序生命周期内只会调用一次。

Student
添加分类如下:

@interface Student (Score)
@end
@implementation Student (Score)
+ (void)initialize {
   NSLog(@"%s", __FUNCTION__);
}
@end
调用的地方修改如下:

@implementation ViewController
- (void)viewDidLoad {
   [super viewDidLoad];
   Student *student = [[Student alloc] init];
}
@end
那么输出是什么呢? 如下:

  +[People initialize]

  +[Student(Score) initialize]

从该实例可以看出,分类的
+initialize
方法会覆盖原类中
+initialize
方法。

load初探

先看看
NSObject Class Reference 中关于
+ (void)load
的说明:

> Invoked whenever a class or category is added to the Objective-C runtime; implement this method to perform class-specific behavior upon loading.> > 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.>

The order of initialization is as follows:

All initializers in any framework you link to.
All +load methods in your image.
All C++ static initializers and C/C++ attribute(constructor) functions in your image.
All initializers in frameworks that link to you.
In addition:

A class’s +load method is called after all of its superclasses’ +load methods.
A category +load method is called after the class’s own +load method.
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.

从上面的描述可以看出:

+ (void)load
会在类或者类的分类添加到
Objective-c runtime
时调用,该调用发生在
application:willFinishLaunchingWithOptions:
调用之前调用。
父类的
+load
方法先于子类的
+load
方法调用,类本身的
+load
方法调用先于分类的
+load
方法调用。
看看下面的示例:

//父类
@interface People : NSObject
@end@implementation People
+ (void)initialize {
   NSLog(@"%@ , %s", [self class], __FUNCTION__);
}
+ (void)load {
   NSLog(@"%@, %s", [self class], __FUNCTION__);
}
@end
//子类

@interfaceStudent :People

@end

@implementationStudent

+ (void)initialize {

  NSLog(@"%@, %s", [self class], __FUNCTION__);

}


+ (void)load {

  NSLog(@"%@, %s", [self class], __FUNCTION__);

}


@end

//子类分类

@interfaceStudent (Score)

@end

@implementationStudent (Score)

+ (void)initialize {

  NSLog(@"%@, %s", [self class], __FUNCTION__);

}


+ (void)load {

  NSLog(@"%@ %s", [self class], __FUNCTION__);

}


@end

控制台输出为:

  People , +[People initialize]

  People, +[People load]

  Student, +[Student(Score) initialize]

  Student, +[Student load]

  Student +[Student(Score) load]

示例2:

//父类

@interfacePeople :NSObject

@end

@implementationPeople

+ (void)initialize {

  NSLog(@"%@ , %s", [self class], __FUNCTION__);

}

@end

//子类

@interfaceStudent :People

@end

@implementationStudent

+ (void)load {

  NSLog(@"%@, %s", [self class], __FUNCTION__);

}


@end

控制台输出为:

  People , +[People initialize]

  Student , +[People initialize]

  Student, +[Student load]

当子类没有实现
+initialize
而父类有其实现时,父类的实现调用了两次,且
+initialize
的调用在
+load
调用之前,这是因为我们在
+load
实现中包含
[self class]
的调用。

另外, 重写
+initialize
+load
方法时,没有必要显示调用其父类的方法。

总结:

 +(void)load+(void)initialize
执行时机在程序运行后立即执行在类的方法第一次被调时执行
若自身未定义,是否沿用父类的方法?
类别中的定义全都执行,但后于类中的方法覆盖类中的方法,只执行一个

load的实际应用实例

每一个项目中在- application:didFinishLaunchingWithOptions:
中的代码都只是为了在程序启动时获得一次调用机会,有些为某些模块的初始化工作。

对于这些模块的初始化工作可以利用
Notification
和load方式在自己的模块内部搞定以达到appdelegate的瘦身。

/// Module.m
+ (void)load
{
__block id observer =
[[NSNotificationCenter defaultCenter]
addObserverForName:UIApplicationDidFinishLaunchingNotification
object:nil
queue:nil
usingBlock:^(NSNotification *note) {
[self setup]; // Do whatever you want
[[NSNotificationCenter defaultCenter] removeObserver:observer];
}];
}

解释:

+ load
方法在足够早的时间点被调用
block 版本的通知注册会产生一个
__NSObserver *
对象用来给外部 remove 观察者
block 对 observer 对象的捕获早于函数的返回,所以若不加
__block
,会捕获到 nil
在 block 执行结束时移除 observer,无需其他清理工作
这样,在模块内部就完成了在程序启动点代码的挂载

值得注意的是,通知是在
- application:didFinishLaunchingWithOptions:
调用完成后才发送的。

顺便提下给 AppDelegate 瘦身的建议:AppDelegate 作为程序级状态变化的 delegate,应该只做路由分发的作用,具体逻辑实现代码还是应该在分别的模块中,这个文件应该保持整洁,除了
<UIApplicationDelegate>
的方法外不应该出现其他方法。

load还可以结合Method Swizzling解决我们实际开发中诸多常规手段所无法解决的问题, 比如代码的插桩,Hook,Patch等等.

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: