初探+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 ...
}
}
>
initializeis invoked only once per class. If you want to perform independent initialization for the class and for categories of the class, you should implement
loadmethods.
从上面的说明可以看出:
+ (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等等.
相关文章推荐
- final关键字详解
- spring boot 支持jar包运行jsp
- Codeforces 388B Fox and Minimal path(构造)
- Android通用的EmptyLayout-展示不用状态的界面
- Linux上怎么快速删除一个目录
- Python Day6
- C# 打开文件或打开文件夹
- java多线程-线程通信
- 待转移
- Android MPAndroidChart使用教程和源码分析(二)
- 解决sqlite3插入数据很慢的问题
- 初识cocos2d-x
- STM32关于串口发送缓冲的问题
- C语言学习 ,一个球从100米的高度自由落下,每次落地后反弹回到原高度的一半,再落下,再反弹,求它在第一次落地时,共经过多少米,第10次反弹多高
- Android Studio使用Material主题样式
- 微信开发:接入微信入口
- Leetcode: Merge Two Sorted Lists
- 抛弃默认签名,使用自己的签名*
- openfire集群+nginx负载均衡
- 剑指Offer26 字符串的全排列