我们先创建一个Person类和它的两个分类
@interface Person : NSObject
+ (void)test;
@end
@implementation Person
+ (void)load{
NSLog(@"Person + load");
}
+ (void)test{
NSLog(@"Person + test");
}
@end
@interface Person (Test1)
+ (void)test;
@end
@implementation Person (Test1)
+ (void)load{
NSLog(@"Person(Test1) + load");
}
+ (void)test{
NSLog(@"Person(Test1) + test");
}
@end
@interface Person (Test2)
+ (void)test;
@end
@implementation Person (Test2)
+ (void)load{
NSLog(@"Person(Test2) + load");
}
+ (void)test{
NSLog(@"Person(Test2) + test");
}
@end
我们调用[Person test];
方法,打印如下:
2020-05-12 15:58:29.899321+0800 XMGTestProject[11526:7291123] Person + load
2020-05-12 15:58:29.900233+0800 XMGTestProject[11526:7291123] Person(Test1) + load
2020-05-12 15:58:29.900414+0800 XMGTestProject[11526:7291123] Person(Test2) + load
2020-05-12 15:58:29.900545+0800 XMGTestProject[11526:7291123] Person(Test2) + test
这里只打印了Test2分类的方法,因为编译时顺序Test2分类比Test1分类后编译。但是注意,每个类的+load方法都执行了一次这是为什么?我们查看runtime源码:
void call_load_methods(void)
{
static bool loading = NO;
bool more_categories;
loadMethodLock.assertLocked();
// Re-entrant calls do nothing; the outermost call will finish the job.
if (loading) return;
loading = YES;
void *pool = objc_autoreleasePoolPush();
do {
// 1. Repeatedly call class +loads until there aren't any more
//首先会调用类的+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);
objc_autoreleasePoolPop(pool);
loading = NO;
}
由源码可知,每个类的+load方法都将调用一次。
我们点进去call_class_loads()方法
static void call_class_loads(void)
{
int i;
// Detach current loadable list.
//数组里面存放所有类
struct loadable_class *classes = loadable_classes;
int used = loadable_classes_used;
loadable_classes = nil;
loadable_classes_allocated = 0;
loadable_classes_used = 0;
// 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_method)(cls, SEL_load);
}
// Destroy the detached list.
if (classes) free(classes);
}
我们看到+load方法的调用跟普通的方法调用不一样,它是直接从类里面拿到这个方法的地址,然后直接调用。所以每个类的+load方法都会调用。
+load方法的调用顺序
由上面源码可知,很显然+load方法的调用顺序是先调用原有类的+load方法,然后再调用分类的+load方法。那么原有类的+load方法调用顺序呢?如果有多个分类其调用顺序又是怎样呢?
先看原有类的+load方法调用顺序
void prepare_load_methods(const headerType *mhdr)
{
size_t count, i;
runtimeLock.assertWriting();
classref_t *classlist =
_getObjc2NonlazyClassList(mhdr, &count);
for (i = 0; i < count; i++) {
//将类放入数组中
schedule_class_load(remapClass(classlist[i]));
}
category_t **categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);
for (i = 0; i < count; i++) {
category_t *cat = categorylist[i];
Class cls = remapClass(cat->cls);
if (!cls) continue; // category for ignored weak-linked class
realizeClass(cls);
assert(cls->ISA()->isRealized());
add_category_to_loadable_list(cat);
}
}
static void schedule_class_load(Class cls)
{
if (!cls) return;
assert(cls->isRealized()); // _read_images should realize
if (cls->data()->flags & RW_LOADED) return;
// Ensure superclass-first ordering
//递归,将父类传进去,把父类加进数组中
schedule_class_load(cls->superclass);
add_class_to_loadable_list(cls);
cls->setInfo(RW_LOADED);
}
从上面schedule_class_load()方法中可以看到会优先调用父类的+load方法。如果没有继承关系,那么就会按照编译的顺序去调用,先编译,先调用。
+load方法调用顺序总结:
1、首先是调用类的+load方法,按类的编译顺序调用,优先编译的先调用,调用子类的+load方法之前优先调用父类的+load方法
2、然后调用分类的+load方法,按分类的编译顺序调用,优先编译的先调用
面试题
1、Category中有load方法吗?load方法是什么时候调用?load方法能继承吗?
答:有load方法,load方法在runtime加载类、分类的时候调用,是可以继承的,但是一般情况下是不会主动调用load方法
网友评论