一、+load方法
@implementation Person
+(void)load{
NSLog(@"%s",__func__);
}
+(void)test{
NSLog(@"%s",__func__);
}
@end
@implementation Person (test1)
+(void)load{
NSLog(@"%s",__func__);
}
+(void)test{
NSLog(@"%s",__func__);
}
@end
@implementation Person (test2)
+(void)load{
NSLog(@"%s",__func__);
}
+(void)test{
NSLog(@"%s",__func__);
}
@end
执行上面代码我们+test符合我们上面提及的方法覆盖,但类和分类的+load方法都执行了,即分类中的+load没有覆盖原类中的+load方法。
void printSelectorNameOfClass(Class cls){
unsigned int count;
Method *methodList = class_copyMethodList(cls, &count);
for (int i = 0; i<count; i++) {
Method method = methodList[i];
NSString * methodName = NSStringFromSelector(method_getName(method));
IMP imp = method_getImplementation(method);
NSLog(@"%@",methodName);
// NSLog(@"%p", imp);
}
NSLog(@"done");
free(methodList);
}
printSelectorNameOfClass(object_getClass([Person class]));
通过上面的代码打印出Person原类对象中的方法列表,发现runtime也把分类中的+load方法合并到了原类对象中。那为何+load方法执行了多次呢???
void call_load_methods(void)
{
do {
// 1. 调用类的+load方法
while (loadable_classes_used > 0) {
call_class_loads();
}
// 2. Call category +loads ONCE 调用类的分类的+load方法
more_categories = call_category_loads();
// 3. Run more +loads if there are classes OR more untried categories
} while (loadable_classes_used > 0 || more_categories);
}
//调用所有类的+load方法
static void call_class_loads(void)
{
int i;
// Detach current loadable list.
struct loadable_class *classes = loadable_classes; //它生成规则决定了原类+load方法的执行顺序。
int used = loadable_classes_used;
// Call all +loads for the detached list. 遍历所有的类
for (i = 0; i < used; i++) {
Class cls = classes[i].cls;
//取出函数指针
load_method_t load_method = (load_method_t)classes[i].method;
if (!cls) continue;
if (PrintLoading) {
_objc_inform("LOAD: +[%s load]\n", cls->nameForLogging());
}
//直接调用+load方法
(*load_method)(cls, @selector(load));
}
// Destroy the detached list.
if (classes) free(classes);
}
static bool call_category_loads(void)
{
struct loadable_category *cats = loadable_categories;//它生成规则决定了分类+load方法的执行顺序。
int used = loadable_categories_used;
// Call all +loads for the detached list.
for (i = 0; i < used; i++) {
Category cat = cats[i].cat;
//取出函数指针
load_method_t load_method = (load_method_t)cats[i].method;
Class cls;
if (!cat) continue;
cls = _category_getClass(cat);
if (cls && cls->isLoadable()) {
if (PrintLoading) {
_objc_inform("LOAD: +[%s(%s) load]\n",
cls->nameForLogging(),
_category_getName(cat));
}
//直接调用+load方法
(*load_method)(cls, @selector(load));
cats[i].cat = nil;
}
}
}
loadable_classes和loadable_categories生成规则在方法prepare_load_methods中,可以看到loadable_classes生成过程即考虑编译顺序,又考虑继承关系,loadable_categories只跟分类的编译顺序相关。
注意:通过上面的源码我们发现系统调用+load不是通过消息发送机制,而且先执行所有类的+load方法,再去执行所有分类的+load方法。
结论:1.+load方法会在runtime加载类、分类时调用。2.每个类、分类的+load方法,在程序运行过程中就调用一次。3.先执行所有原类的+load方法,再去执行所有分类的+load方法。4.原类的+load方法按照编译顺序执行(先编译,先调用),也遵循调用原类的+load方法,先调用其父类的+load方法。5.分类的+load执行顺序按照编译顺序执行(先编译,先调用)。
二、+initialize方法
@implementation Person
+(void)initialize{
NSLog(@"%s",__func__);
}
@end
@implementation Person (test1)
+(void)initialize{
NSLog(@"%s",__func__);
}
@end
@implementation Student
//+(void)initialize{
// NSLog(@"%s",__func__);
//}
@end
@implementation Teacher
//+(void)initialize{
// NSLog(@"%s",__func__);
//}
@end
其中Teacher、Student类继承于Person类
[Student alloc];
[Teacher alloc];
发现Person的分类中的+initialize方法执行了3次!下面我们来探究下+initialize方法的调用规则。
消息发送的方法查找过程中调用了+initialize方法。
//查找类方法,
Method class_getClassMethod(Class cls, SEL sel)
{
if (!cls || !sel) return nil;
//把元类对象传进去
return class_getInstanceMethod(cls->getMeta(), sel);
}
//查找实例方法
Method class_getInstanceMethod(Class cls, SEL sel)
{
if (!cls || !sel) return nil;
// Search method lists, try method resolver, etc.
lookUpImpOrForward(nil, sel, cls, LOOKUP_RESOLVER);
#warning fixme build and search caches
return _class_getMethod(cls, sel);
}
lookUpImpOrForward--》initializeAndLeaveLocked--》initializeAndMaybeRelock--》initializeNonMetaClass
void initializeNonMetaClass(Class cls)
{
ASSERT(!cls->isMetaClass());
Class supercls;
bool reallyInitialize = NO;
// 调用本类的initialize前递归调用其父类的initialize
supercls = cls->superclass;
if (supercls && !supercls->isInitialized()) {
initializeNonMetaClass(supercls);
}
// Try to atomically set CLS_INITIALIZING.
SmallVector<_objc_willInitializeClassCallback, 1> localWillInitializeFuncs;
{
monitor_locker_t lock(classInitLock);
//本类被初始化并且没有正在初始化才能调用initialize
if (!cls->isInitialized() && !cls->isInitializing()) {
cls->setInitializing();
//标示可以调用initialize
reallyInitialize = YES;
localWillInitializeFuncs.initFrom(willInitializeFuncs);
}
}
if (reallyInitialize) {
{
//调用initialize方法
callInitialize(cls);
}
return;
}
else if (cls->isInitialized()) {
//本类已经被初始化了,不在调用initialize
return;
}
}
//这里才真正调用initialize方法
void callInitialize(Class cls)
{
//通过objc_msgSend调用initialize方法
((void(*)(Class, SEL))objc_msgSend)(cls, @selector(initialize));
}
结论:1.initialize方法是通过消息发送调用的。2.+initialize方法会在类第一次接收到消息时调用。3.先调用父类+initialize方法,再调用子类的+initialize方法。4.当子类没有实现+initialize方法,会去调用父类的+initialize方法(父类的initialize方法可能会调用多次)。5.如果分类实现了+initialize,会覆盖原有类的+initialize方法。1,2,3通过源码可以看出来,4,5是通过消息发送机制决定的。
三、两者比较
load、initialize方法的区别
1.调用方式
1》load是根据函数地址直接调用
2》initialize是通过objc_msgSend调用
2.调用时刻
1》load是runtime加载类、分类的时候调用(只会调用一次)
2》initialize是类第一次接受到消息进行初始化时调用。
3.调用顺序
1》load
1)先调用所有类的load; 排序规则:A先编译的类,优先调用load;B调用子类的load前,回先调用父类的load
2)在调用所有分类的load; 排序规则:先编译的分类,优先调用load
2》initialize :先调用父类的initialize,在调用子类的initialize。由于每个类只会初始化一次,原则上每个类的initialize都只会调用一次,但当子类没有实现initialize方法,子类初始化时要调用initialize方法就会进行方法查找,进而去调用父类的initialize方法,所以父类的initialize可能会调用多次。