iOS原理(四)----+load,+initialize
+load
+load
方法会在runtime加载类、分类时调用,每个类、分类的+load,在程序运行过程中只调用一次.
创建Animal
及其Run
,Sleep
分类,分别实现+load
方法
@interface Animal : NSObject
@end
@implementation Animal
+ (void)load {
NSLog(@"Animal load");
}
@end
@interface Animal (Run)
@end
@implementation Animal (Run)
+ (void)load {
NSLog(@"Animal (Run) load");
}
@end
@interface Animal (Sleep)
@end
@implementation Animal (Sleep)
+ (void)load {
NSLog(@"Animal (Sleep) load");
}
@end
编译顺序如下:
Snip20181110_9.png根据前面已知Category
原理,应该打印Sleep
分类的+load
方法,运行程序打印如下:
先调用Animal
类的+load
,再调用Run
分类的+load
,最后才调用Sleep
分类的+load
.继续寻找源码,发现如下:
void
load_images(const char *path __unused, const struct mach_header *mh)
{
// Return without taking locks if there are no +load methods here.
if (!hasLoadMethods((const headerType *)mh)) return;
recursive_mutex_locker_t lock(loadMethodLock);
// 获取所有+load方法
{
rwlock_writer_t lock2(runtimeLock);
prepare_load_methods((const headerType *)mh);
}
// 调用所有+load方法
call_load_methods();
}
// 获取所有的+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);
}
}
// 处理所有类的+load方法
static void schedule_class_load(Class cls)
{
if (!cls) return;
assert(cls->isRealized()); // _read_images should realize
if (cls->data()->flags & RW_LOADED) return;
// 确保父类在顺序的前面
schedule_class_load(cls->superclass);
// 添加到loadable_list中
add_class_to_loadable_list(cls);
cls->setInfo(RW_LOADED);
}
// 调用+load方法
void call_load_methods(void)
{
static bool loading = NO;
bool more_categories;
loadMethodLock.assertLocked();
if (loading) return;
loading = YES;
void *pool = objc_autoreleasePoolPush();
do {
// 1.调用类的+load方法
while (loadable_classes_used > 0) {
call_class_loads();
}
// 2.调用分类的+load方法
more_categories = call_category_loads();
} while (loadable_classes_used > 0 || more_categories);
objc_autoreleasePoolPop(pool);
loading = NO;
}
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;
// 调用所有的类+load方法
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);
}
从上面的代码得知:
- 1.先调用类的
+load
方法,再调用分类的+load
方法按编译顺序调用. - 2.类的
+load
方法,先调用父类的+load
. - 3.类的
+load
方法,按照编译编译顺序调用.
前面的打印证明了第一点,接下来证明第2,第3点.
新建Cat
类继承Animal
,
@interface Cat : Animal
@end
@implementation Cat
+ (void)load {
NSLog(@"Cat load");
}
@end
打印如下
Snip20181110_2.png
第2点得到证明.
新建Dog类
类继承Animal
,
@interface Dog : Animal
@end
@implementation Dog
+ (void)load {
NSLog(@"Dog load");
}
@end
编译如图:
Snip20181110_11.png
打印如下:
Snip20181110_12.png
第3点得到证明.
+initialize方法
+initialize
方法会在类第一次接收到消息时调用.
注释掉前面所有类的+load
方法,实现+initialize
方法(简单打印改方法).
编译文件和顺序如图:
使用Cat
和Dog
两个类.
打印如下:
Snip20181110_15.png可以知道先调用Animal``sleep
分类的+initialize
,再调用Cat
的+initialize
,再调用Dog
的+initialize
.我们可以从苹果源码中寻找答案,
lookUpImpOrForward()
有一段实现如下;
// 需要初始化和该类并没有初始化时
if (initialize && !cls->isInitialized()) {
runtimeLock.unlockRead();
_class_initialize (_class_getNonMetaClass(cls, inst));
runtimeLock.read();
// If sel == initialize, _class_initialize will send +initialize and
// then the messenger will send +initialize again after this
// procedure finishes. Of course, if this is not being called
// from the messenger then it won't happen. 2778172
}
而_class_initialize()
有一段代码实现如下:
// superclass存在且superclass没有初始化时
if (supercls && !supercls->isInitialized()) {
_class_initialize(supercls);
}
{
callInitialize(cls);
if (PrintInitializing) {
_objc_inform("INITIALIZE: thread %p: finished +[%s initialize]",
pthread_self(), cls->nameForLogging());
}
}
callInitialize()实现如下:
void callInitialize(Class cls)
{
((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
asm("");
}
可以看出+initialize
是通过objc_msgSend
进行调用的.得出结论:
- 先调用父类的
+initialize
,再调用子类的+initialize
. - 如果分类实现了就调用分类的
+initialize
,按照后编译先调用原则.
网友评论