Runtime

Runtime - load

2021-07-27  本文已影响0人  ienos

阅读 objc4-master 源码中 +load()

参考链接


思考

源码阅读

1. 追溯 +load()
+load()

可以看到主要有三个函数,如下:

>> 通过源码阅读,了解到函数的调用树如下:

image.png
2. load_images 来源 _objc_init
void _objc_init(void)
{
    ...
    dyld_register_image_state_change_handler(dyld_image_state_dependents_initialized, 0/*not batch*/, &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()
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()
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() 的全局变量

struct loadable_class {
    Class cls;  // may be nil
    IMP method;
};
struct loadable_category {
    Category cat;  // may be nil
    IMP method;
};
typedef void(*load_method_t)(id, SEL);
7. Class 的 +load() 和 Category 的 +load()

√ 那么 loadable_class 和 loadable_category 对应的 method 是怎么加载的?

loadable_class
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
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_classloadable_category 对应的函数指针是不一致的

上一篇 下一篇

猜你喜欢

热点阅读