要想收获更多知识,还是得追其根源。OC 中有两个特殊的类方法,分别是 load 和 initialize, 本文主要总结一下这两个方面的区别与联系、使用场景和注意事项。
load
顾名思义,load 方法在这个文件被程序装载时调用,即当程序被载入内存时调用。只要是在 Compile Sources 中出现的文件总是会被装载,这与这个类是否被用到无关,因此 load 方法总是在 main 函数之前调用。
注意点:所谓 Compile Sources,详细见下图:
调用规则
如果一个类实现了 load 方法,在调用这个方法前会首先调用父类的 load 方法。而且这个过程是自动完成的,并不需要我们手动实现:
父类 :
#import "Father.h"
@implementation Father
+(void)load
{
NSLog(@"Load Class Father");
}
@end
子类:
#import "Children.h"
@implementation Children
+(void)load
{
NSLog(@"Load Class Children");
}
@end
子类分类:
#import "Children+load.h"
@implementation Children (load)
+(void)load
{
NSLog(@"Load Class Child+load");
}
@end
运行结果:
执行顺序
执行子类的 load 方法前,会先执行所有超类的 load 方法,顺序为:父类 -> 子类 -> 分类,这一点由上图也可以看出来。
load 方法调用时,系统处于脆弱状态,如果调用别的类的方法,且该方法依赖于那个类的 load 方法进行初始化设置,那么久必须确保那个类的 load 方法已经调用了。
使用场景
由于调用 load 方法时的环境很不安全,我们应该尽量减少 load 方法的逻辑。load 方法中最常用的就是方法交换,即 Method Swizzle。
initialize
在首次使用该类之前由运行期系统(非人为)调用,且仅调用一次;为惰性调用,也就是说如果一个类一直没被用到,那它的 initialize 方法也不会被调用,这一点有利于节约资源。
调用规则
与 load 方法类似的是,在 initialize 方法内部也会调用父类的方法,而且不需要我们显示的写出来。与 load 方法不同之处在于,即使子类没有实现 initialize 方法,也会调用父类的方法(原因:在创建子类对象时,首先要创建父类对象,所以会调用一次父类的 initialize 方法),这回导致很严重的问题。
正确使用 initialize 方法的姿势如下:
父类:
+ (void)initialize {
if (self == [Father class]) {
NSLog(@"Initialize Father, caller Class %@", [self class]);
}
}
加上判断后,就不会因为子类而调用到自己的 initialize 方法了。
使用场景
initialize 方法一般用于初始化全局变量或静态变量。例如设置整个 APP 的统一导航条及相关属性:
+ (void)initialize
{
if (self == [FGNavigationController class]) {
// 获取当前类下面的导航条
UINavigationBar *navBar = [UINavigationBar appearanceWhenContainedIn:self, nil];
// 设置导航条背景图片
[navBar setBackgroundImage:[UIImage imageNamed:@"NavBar64"] forBarMetrics:UIBarMetricsDefault];
// 设置导航条文字标题
NSMutableDictionary *textAttr = [NSMutableDictionary dictionary];
// 颜色
textAttr[NSForegroundColorAttributeName] = [UIColor whiteColor];
textAttr[NSFontAttributeName] = [UIFont boldSystemFontOfSize:17];
// titleTextAttributes:给标题文字设置属性,(颜色,字体,阴影....)
navBar.titleTextAttributes = textAttr;
}
}
总结