在介绍之前,我们首先来了解一下类的使用,我们要使用一个类,大概要经过以下步骤
- 启动App,程序开始加载类到内存中(代码区)+(void)load
- 首次使用该类时,创建类对象(我们可以把它看作是一个单例,它在整个程序中只有一份)+(void)initialize
- 通过类对象创建实例对象+(instancetype)alloc、-(instancetype)init
- 通过实例对象,我们就可使用实例方法、类属性了
从上面的步骤我们也大概了解到load和initialize的调用时机了,下面在来详细说一下
一、+load
类方法
当类被引用进项目的时候就会执行load函数(在main函数开始执行之前),与这个类是否被用到无关,每个类的load函数只会自动调用一次.由于load函数是系统自动加载的,因此不需要再调用[super load],否则父类的load函数会多次执行。
- 1.当父类和子类都实现load函数时,父类的load方法执行顺序要优先于子类
- 2.当一个类未实现load方法时,不会调用父类load方法
- 3.类中的load方法执行顺序要优先于类别(Category)
- 4.当有多个类别(
Category
)都实现了load
方法,这几个load方法都会执行,但执行顺序不确定(其执行顺序与类别在Compile Sources中出现的顺序一致) - 5.当然当有多个不同的类的时候,每个类load 执行顺序与其在Compile Sources出现的顺序一致
二、+initialize
类方法
- 即使类文件被引用进项目,但是没有使用,initialize不会被调用
- 假如这个类放到代码中,而这段代码并没有被执行,这个函数是不会被执行的。
- 类或者其子类的第一个方法被调用前调用
- 由于是系统自动调用,也不需要再调用
[super initialize]
,否则父类的initialize
会被多次执行 - 父类的
initialize
方法会比子类先执行 - 子类未实现
initialize
方法时,会调用父类initialize
方法,子类实现initialize
方法时,会覆盖父类initialize
方法. - 当有多个
Category
都实现了initialize
方法,会覆盖类中的方法,只执行一个(会执行Compile Sources
列表中最后一个Category
的initialize
方法)
三、两者的异同
1、相同点
load
和initialize
会被自动调用,不能手动调用它们。- 如果父类和子类都被调用,父类的调用一定在子类之前
- 子类实现了
load
和initialize
的话,会隐式调用父类的load和initialize方法。 load
和initialize
方法内部使用了锁,因此它们是线程安全的。2、不同点
调用顺序不同,以
main
函数为分界,+load
方法在main函数之前执行,+initialize
在main函数之后执行。- 子类中没有实现
+load
方法的话,不会调用父类的+load
方法;而子类如果没有实现+initialize
方法的话,也会自动调用父类的+initialize
方法。 +load
方法是在类被装在进来的时候就会调用,+initialize
在第一次给某个类发送消息时调用(比如实例化一个对象),并且只会调用一次,是懒加载模式,如果这个类一直没有使用,就不会调用到+initialize
方法。四、使用场景
1.
+load
一般是用来交换方法Method Swizzle
,由于它是线程安全的,而且一定会调用且只会调用一次,通常在使用UrlRouter的时候注册类的时候也在+load
方法中注册。 ```objectivec +(void)load {static dispatch_once_t oneToken; dispatch_once(&oneToken, ^{
Method imageNamed = class_getClassMethod(self,@selector(imageNamed:));
Method mkeImageNamed =class_getClassMethod(self,@selector(swizze_imageNamed:)); method_exchangeImplementations(imageNamed, mkeImageNamed);
}); }
(instancetype)swizze_imageNamed:(NSString*)name {
//这个时候swizze_imageNamed已经和imageNamed交换imp,所以当你在调用swizze_imageNamed时候其实就是调用imageNamed UIImage * image; if( IS_IPHONE ){
// iphone处理
UIImage * image = [self swizze_imageNamed:name];
if (image != nil) {
return image;
} else {
return nil;
}
} else {
// ipad处理,_ipad是自己定义,~ipad是系统自己自定义。 UIImage *image = [self swizze_imageNamed:[NSString stringWithFormat:@"%@_ipad",name]]; if (image != nil) { return image; }else { image = [self swizze_imageNamed:name]; return image; }
} ```
2.+initialize
方法主要用来对一些不方便在编译期初始化的对象进行赋值,或者说对一些静态常量进行初始化操作。
/ In Person.m
// int类型可以在编译期赋值
static int someNumber = 0;
static NSMutableArray *someArray;
+ (void)initialize {
if (self == [Person class]) {
// 不方便编译期复制的对象在这里赋值
someArray = [[NSMutableArray alloc] init];
}
}
五、总结
- load和initialize方法都会在实例化对象之前调用
- load执行在main函数以前,initialize执行main函数之后
- 这两个方法会被自动调用,不能手动调用它们。
- load和initialize方法都不用显示的调用父类的方法而是自动调用
- 子类没有initialize方法也会调用父类的方法,而load方法则不会调用父类
- initialize方法对一个类而言只会调用一次(Person、或者是Person+Category)都是一个Perosn类。load方法则是每个都会调用,只要你写了load方法,添加到工程都会实现。
- load方法通常用来进行Method Swizzle,initialize方法一般用于初始化全局变量或静态变量。
- load和initialize方法内部使用了锁,因此它们是线程安全的。实现时要尽可能保持简单,避免阻塞线程,不要再使用锁。