iOS底层原理之load_images分析
2020-02-09 本文已影响0人
尘舒
1. load_images的调用时机
- 在objc_init中,进行完一系列配置初始化完成之后
- 调用_dyld_objc_notify_register方法
- map_images调用
- load_images调用
void _objc_init(void)
{
static bool initialized = false;
if (initialized) return;
initialized = true;
// fixme defer initialization until an objc-using image is found?
environ_init();
tls_init();
static_init();
lock_init();
exception_init();
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
}
2. load_images方法
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);
// Discover load methods
{
mutex_locker_t lock2(runtimeLock);
prepare_load_methods((const headerType *)mh);
}
// Call +load methods (without runtimeLock - re-entrant)
call_load_methods();
}
2.1 准备阶段
-首先,使用prepare_load_methods方法去发现准备非懒加载的类和分类
- 懒加载的类在编译阶段已经确定了,所以只需要获取非懒加载类列表
void prepare_load_methods(const headerType *mhdr)
{
size_t count, i;
runtimeLock.assertLocked();
// 获取非懒加载的类列表
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
if (cls->isSwiftStable()) {
_objc_fatal("Swift class extensions and categories on Swift "
"classes are not allowed to have +load methods");
}
realizeClassWithoutSwift(cls);
assert(cls->ISA()->isRealized());
add_category_to_loadable_list(cat);
}
}
2.1.1 非懒加载类的处理
- 1._getObjc2NonlazyClassList获取类列表
- 2.遍历类列表,使用schedule_class_load对类进行处理
// Recursively schedule +load for cls and any un-+load-ed superclasses.
// cls must already be connected.
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);
}
- 3.使用add_class_to_loadable_list添加类到已经加载的类表中去
/***********************************************************************
* add_class_to_loadable_list
* Class cls has just become connected. Schedule it for +load if
* it implements a +load method.
**********************************************************************/
void add_class_to_loadable_list(Class cls)
{
IMP method;
loadMethodLock.assertLocked();
method = cls->getLoadMethod();
if (!method) return; // Don't bother if cls has no +load method
if (PrintLoading) {
_objc_inform("LOAD: class '%s' scheduled for +load",
cls->nameForLogging());
}
// loadable_list创建以及扩容,采用2倍扩容
if (loadable_classes_used == loadable_classes_allocated) {
loadable_classes_allocated = loadable_classes_allocated*2 + 16;
loadable_classes = (struct loadable_class *)
realloc(loadable_classes,
loadable_classes_allocated *
sizeof(struct loadable_class));
}
// loadable_list开始存储当前类cls以及method
loadable_classes[loadable_classes_used].cls = cls;
loadable_classes[loadable_classes_used].method = method;
loadable_classes_used++;
}
- 4.使用method = cls->getLoadMethod()来获取当前类的load方法
- 5.使用loadable_class结构体,来存储当前类cls和当前类的load方法,并且用一个数组loadable_classes来存放整体的非懒加载类的列表
struct loadable_class {
Class cls; // may be nil
IMP method;
};
2.1.2 非懒加载分类的处理
- 1._getObjc2NonlazyCategoryList获取分类列表
- 2.遍历分类列表,使用add_category_to_loadable_list把列表存储到另外一个loadable_list中loadable_categories
- 3.内部原理和上面存储类一样,
// 具体存储方法
loadable_categories[loadable_categories_used].cat = cat;
loadable_categories[loadable_categories_used].method = method;
// 该结构体存储分类cat以及method
struct loadable_category {
Category cat; // may be nil
IMP method;
};
2.1.3 小结
- 准备阶段到此结束,发现并且遍历处理,使用两个数组loadable_classes以及loadable_categories存放所有的非懒加载类和分类
2.2 调用阶段
- call_load_methods方法,方法内部使用一个do-while循环来保证类和分类的load方法能够全部被调用,loadable_classes_used>0代表存在着多个非懒加载类,那么就有多个类的load方法已经被开发者自己实现了
- load方法 就是在这里调用的
do {
// 1. Repeatedly call class +loads until there aren't any more
while (loadable_classes_used > 0) {
call_class_loads();
}
// 2. Call category +loads ONCE
more_categories = call_category_loads();
// 3. Run more +loads if there are classes OR more untried categories
} while (loadable_classes_used > 0 || more_categories);
- call_class_loads,类的load方法进行调用
- call_category_loads,分类load方法的调用
- 区别,类的所有分类的load方法调用完成之后,会一个个依次销毁
// Compact detached list (order-preserving)
shift = 0;
for (i = 0; i < used; i++) {
if (cats[i].cat) {
cats[i-shift] = cats[i];
} else {
shift++;
}
}
used -= shift;
// Copy any new +load candidates from the new list to the detached list.
new_categories_added = (loadable_categories_used > 0);
for (i = 0; i < loadable_categories_used; i++) {
if (used == allocated) {
allocated = allocated*2 + 16;
cats = (struct loadable_category *)
realloc(cats, allocated *
sizeof(struct loadable_category));
}
cats[used++] = loadable_categories[i];
}
// Destroy the new list.
if (loadable_categories) free(loadable_categories);
// Reattach the (now augmented) detached list.
// But if there's nothing left to load, destroy the list.
if (used) {
loadable_categories = cats;
loadable_categories_used = used;
loadable_categories_allocated = allocated;
} else {
if (cats) free(cats);
loadable_categories = nil;
loadable_categories_used = 0;
loadable_categories_allocated = 0;
}
2.3 load_imags结束
- 至此,load_images调用结束,_objc_init也调用结束
- 也代表整个dyld阶段结束
3 常见问题
3.1当分类和类有同样的方法时,如何调用
- 如果是load方法,那么主类先调用,分类后调用;
- 如果是其他方法,那么则只有分类的同名方法会调用(会产生类似覆盖的假象,但实际上只是分类方法插入到了主类的方法列表头部产生的)
3.2 initialize调用
- initialize会在类首次进行消息发送时被调用,可以被子类重写
- lookUpImpOrForward->initializeAndLeaveLocked->initializeAndMaybeRelock->initializeNonMetaClass->callInitialize(cls)
void callInitialize(Class cls)
{
((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
asm("");
}
- 最后,来张简单的流程图镇楼~