一、前言
此篇文章主要介绍的底层系列 - load 和 initialize,对于 load 和 initialize 对象我们大多数人都了解,但是对于底层和很多知识点总是容易忘。
二、问题
load方法是什么时候调用的?
- load方法,在runtime加载类、分类的时候调用。
load 方法能继承吗?
- Load 方法可以继承,但是一般情况下不会主动去调用load方法,都是让系统自动调用load。
load、initialize方法的区别什么?
-
+initialize
- 先调用父类,再调用子类(如果分类实现了+initialize,就 覆盖类本身的 +initialize调用),在类第一次收到消息的时候调用(是通过objc_msgSend进行调用的)
-
+load
- 方法是根据方法地址直接调用,并不是经过 objc_msgSend 函数调用。是 通过函数指针指向函数,拿到函数地址,分开来直接调用的(直接通过内存地址查找调用的)
load、initialize方法的特点
- 如果子类没有实现 +initialize,会调用父类的 +initialize(所以父类的 +initialize可能会被调用多次)
- 如果分类实现了 +initialize,就覆盖类本身的 +initialize调用。
三、总结
除了普通类是按照继承必须先调用父类的 super 之外,其它类都是按照编译顺序调用 +load 的。
+load
+initialize
四、相关源码 + load
1.load_images
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);
// Discover load methods
{
mutex_locker_t lock2(runtimeLock);
// 准备 load 方法
prepare_load_methods((const headerType *)mh);
}
// Call +load methods (without runtimeLock - re-entrant)
// 调用 load 方法
call_load_methods();
}
2.prepare_load_methods
void prepare_load_methods(const headerType *mhdr)
{
size_t count, i;
runtimeLock.assertLocked();
// 拿到类的编译顺序
classref_t const *classlist =
_getObjc2NonlazyClassList(mhdr, &count);
// 1. 递归调用,添加父类,和子类
for (i = 0; i < count; i++) {
schedule_class_load(remapClass(classlist[i]));
}
// 2. 添加分类的 loadlist
category_t * const *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
if (cls->isSwiftStable()) {
_objc_fatal("Swift class extensions and categories on Swift "
"classes are not allowed to have +load methods");
}
realizeClassWithoutSwift(cls, nil);
ASSERT(cls->ISA()->isRealized());
add_category_to_loadable_list(cat);
}
}
3. add_category_to_loadable_list
void add_category_to_loadable_list(Category cat)
{
IMP method;
loadMethodLock.assertLocked();
method = _category_getLoadMethod(cat);
// Don't bother if cat has no +load method
if (!method) return;
if (PrintLoading) {
_objc_inform("LOAD: category '%s(%s)' scheduled for +load",
_category_getClassName(cat), _category_getName(cat));
}
if (loadable_categories_used == loadable_categories_allocated) {
loadable_categories_allocated = loadable_categories_allocated*2 + 16;
loadable_categories = (struct loadable_category *)
realloc(loadable_categories,
loadable_categories_allocated *
sizeof(struct loadable_category));
}
loadable_categories[loadable_categories_used].cat = cat;
loadable_categories[loadable_categories_used].method = method;
loadable_categories_used++;
}
五、相关源码 + initialize
1. initializeAndMaybeRelock
static Class initializeAndMaybeRelock(Class cls, id inst,
mutex_t& lock, bool leaveLocked)
{
lock.assertLocked();
ASSERT(cls->isRealized());
// 如果已经调用,那么返回
if (cls->isInitialized()) {
if (!leaveLocked) lock.unlock();
return cls;
}
initializeNonMetaClass(nonmeta);
}
2. initializeNonMetaClass
// class_initialize. Send the '+initialize' message on demand to any
// uninitialized class. Force initialization of superclasses first.
void initializeNonMetaClass(Class cls)
{
ASSERT(!cls->isMetaClass());
Class supercls;
bool reallyInitialize = NO;
// Make sure super is done initializing BEFORE beginning to initialize cls.
// See note about deadlock above.
// 首先调用父类 的 initialize
supercls = cls->superclass;
if (supercls && !supercls->isInitialized()) {
initializeNonMetaClass(supercls);
}
callInitialize(cls);
}
3. callInitialize
// 通过消息发送的方式
void callInitialize(Class cls)
{
((void(*)(Class, SEL))objc_msgSend)(cls, @selector(initialize));
asm("");
}