阅读 objc4-master 源码中 +load()
参考链接
思考
- load 方法的作用是什么?
- load 在什么时候调用?
- load 调用多少次?
- Category 中的 +load() 和 Class 中的 +load() 的区别
源码阅读
1. 追溯 +load()
+load()可以看到主要有三个函数,如下:
- load
- call_load_methods
- load_images
>> 通过源码阅读,了解到函数的调用树如下:
image.png2. load_images 来源 _objc_init
void _objc_init(void)
{
...
dyld_register_image_state_change_handler(dyld_image_state_dependents_initialized, 0/*not batch*/, &load_images);
}
- dyld 中注册一个回调 load_images 对应的函数指针,类型是
dyld_image_state_dependents_initialized
,dyld_image_state_dependents_initialized
是一个初始化方法,load_images
只调用一次。
const char *
load_images(enum dyld_image_states state, uint32_t infoCount,
const struct dyld_image_info infoList[])
{
...
// 发现 load 方法
{
rwlock_writer_t lock2(runtimeLock);
found = load_images_nolock(state, infoCount, infoList);
}
// 调用 load 方法
if (found) {
call_load_methods();
}
return nil;
}
3. call_load_methods,先 class +load() 后 category +load()
void call_load_methods(void)
{
...
do {
// 1. 调用 class 的 +load() 直到 loable_classes_used 的个数为 0
while (loadable_classes_used > 0) {
call_class_loads();
}
// 2. 调用 category 的 +load()
more_categories = call_category_loads();
// 3. 直到所有的 class 和 category 都调用 +load() 跳出循环
} while (loadable_classes_used > 0 || more_categories);
...
}
4. call_category_loads: category +load()
-
call_category_loads
中对全局变量loadable_categories
中的 category 对应的 class 逐一执行 SEL_load
static bool call_category_loads(void)
{
int i, shift;
bool new_categories_added = NO;
// 全局的 loadable_categories
struct loadable_category *cats = loadable_categories;
int used = loadable_categories_used;
int allocated = loadable_categories_allocated;
loadable_categories = nil;
loadable_categories_allocated = 0;
loadable_categories_used = 0;
// 调用 loadable_categories 中的 category 的 load 方法
for (i = 0; i < used; i++) {
Category cat = cats[i].cat;
load_method_t load_method = (load_method_t)cats[i].method;
Class cls;
if (!cat) continue;
cls = _category_getClass(cat);
// 需要确保 category 对应的 class 已经加载,才会调用 category 的 +load()
if (cls && cls->isLoadable()) {
if (PrintLoading) {
_objc_inform("LOAD: +[%s(%s) load]\n",
cls->nameForLogging(),
_category_getName(cat));
}
// SEL_load
(*load_method)(cls, SEL_load);
cats[i].cat = nil;
}
}
// 重新排列未加载的 category 到 loadable_catgories
...
}
5. call_class_loads: class +load()
-
call_class_loads
中对全局变量loadable_classes
中的 class 逐一执行 SEL_load
static void call_class_loads(void)
{
int i;
// 全局的 loadable_classes
struct loadable_class *classes = loadable_classes;
int used = loadable_classes_used;
loadable_classes = nil;
loadable_classes_allocated = 0;
loadable_classes_used = 0;
// 调用 loadable_classes 中的 class 的 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);
}
6. loadable_classes & loadable_categories 的结构
loadable_classes 和 loadable_categories 是分别为 call_class_loads 和 call_category_loads,指定 class 调用的 +load() 的全局变量
- loadable_classes 添加 loadable_class 结构体的元素
struct loadable_class {
Class cls; // may be nil
IMP method;
};
- loadable_categories 添加 loadable_category 结构体的元素
struct loadable_category {
Category cat; // may be nil
IMP method;
};
- IMP method 均为 load_method_t
typedef void(*load_method_t)(id, SEL);
7. Class 的 +load() 和 Category 的 +load()
-
若假设一个类 ClassA,有一个 Category,
load_Images
时需要调用call_class_loads
和call_category_loads
-
对于
call_class_loads
和call_category_loads
来说,load_method
的参数 class 和 SEL_load 均一致 -
从
call_class_loads
和call_category_loads
代码中可以看出load_method
分别为loadable_class->method
和loadable_category->method
强转过来的
√ 那么 loadable_class 和 loadable_category 对应的 method 是怎么加载的?
-
loadable_class
通过 class->ro->baseMethods 获取 method->name 为 load 的函数指针
void add_class_to_loadable_list(Class cls) {
...
method = cls->getLoadMethod();
...
loadable_classes[loadable_classes_used].method = method;
...
}
IMP
objc_class::getLoadMethod()
{
...
mlist = ISA()->data()->ro->baseMethods();
if (mlist) {
for (const auto& meth : *mlist) {
const char *name = sel_cname(meth.name);
if (0 == strcmp(name, "load")) {
return meth.imp;
}
}
}
return nil;
}
-
loadable_category
通过 category->classMethods 获取 method->name 为 load 的函数指针
void add_category_to_loadable_list(Category cat) {
...
method = _category_getLoadMethod(cat);
...
loadable_categories[loadable_categories_used].method = method;
...
}
IMP
_category_getLoadMethod(Category cat)
{
runtimeLock.assertLocked();
const method_list_t *mlist;
/// 获取
mlist = cat->classMethods;
if (mlist) {
for (const auto& meth : *mlist) {
const char *name = sel_cname(meth.name);
if (0 == strcmp(name, "load")) {
return meth.imp;
}
}
}
return nil;
}
结论: loadable_class
和 loadable_category
对应的函数指针是不一致的
网友评论