前言:
上篇文章 iOS Category底层原理详细研究流程 中 , 我们有说到编译时
dyld
解读Mach-o
文件对应在runtime
库的入口函数_objc_init
中三种不同时期对应的回调函数.那么同样 , 我们探索
load
方法也是由此展开 . 也就是在dyld
读取完成时会调用load_images
方法 ,load
方法也是在此时调用. 感兴趣的可以去解读一下上篇博客.
废话不多说 , 同样打开 objc4
的源码 ( git开源地址 )
探索流程
1. runtime 库入口函数
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);
}
关于 map_images
, load_images
, unmap_image
这三个函数我上一篇博客有具体提到, 这里不多赘述了. 直接进去 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();
}
代码中看出这里分为了两步 , 先准备 , 然后调用 . --> GO
2. load 方法准备
void prepare_load_methods(const headerType *mhdr)
{
size_t count, i;
runtimeLock.assertLocked();
//从 Macho 文件加载类的列表
classref_t *classlist =
_getObjc2NonlazyClassList(mhdr, &count);
for (i = 0; i < count; i++) {
//数组:[<cls,method>,<cls,method>,<cls,method>] 有顺序
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);
}
}
这里又有两步操作
2.1 类的 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;
// Ensure superclass-first ordering
schedule_class_load(cls->superclass);
add_class_to_loadable_list(cls);
cls->setInfo(RW_LOADED);
}
其实就是从 Mach-o
中读取到所有的类的列表 . 然后
- 递归 按当前类的
superclass
指针进行排列. 以此来满足一个规则 :
一个类的
load
方法调用 , 一定是在其父类的load
方法之后.
- 当递归到
NSObject
后 , 再找父类为nil
, 跳出递归 . 结束排列 , 并且将每个load
方法的cls
和method
存储到全局的loadable_class
结构体中.
2.2 分类的 load 方法读取和排序
分类的 load
大体流程和类的基本类似 , 同样先从 Mach-o
读取所有的分类列表 , 然后直接遍历 , 添加分类的 load
方法的 cls
和 method
存储到全局的 loadable_categories
结构体中.
也就是说 , 不同分类之间 load
方法的加载顺序是根据 Mach-o
文件的编译顺序有关.
3. load 调用
call_load_methods
:
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
while (loadable_classes_used > 0) {
//先调用类的 load 方法
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);
objc_autoreleasePoolPop(pool);
loading = NO;
}
这里就比较简单了. 先遍历之前存储好的类的 load
方法列表. 依次调用. 然后遍历分类的列表 , 依次调用.
总结:
load
方法在runtime
库开始运行时调用.- 一个类的
load
方法在所有的父类load
方法调用之后.- 分类的
load
方法在类的load
方法之后.- 不同分类之间的
load
方法调用顺序和编译顺序有关.
今天的文章iOS load方法调用机制解析分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/18135.html