Runtime - load

作者: ienos | 来源:发表于2021-07-27 14:20 被阅读0次

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

    参考链接


    思考

    • load 方法的作用是什么?
    • load 在什么时候调用?
    • load 调用多少次?
    • Category 中的 +load() 和 Class 中的 +load() 的区别

    源码阅读

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

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

    • load
    • call_load_methods
    • load_images

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

    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);
    }
    
    • dyld 中注册一个回调 load_images 对应的函数指针,类型是 dyld_image_state_dependents_initializeddyld_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_loadscall_category_loads

    • 对于 call_class_loadscall_category_loads 来说, load_method 的参数 class 和 SEL_load 均一致

    • call_class_loadscall_category_loads 代码中可以看出 load_method 分别为 loadable_class->methodloadable_category->method 强转过来的

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

    • loadable_class 通过 class->ro->baseMethods 获取 method->name 为 load 的函数指针
    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 通过 category->classMethods 获取 method->name 为 load 的函数指针
    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 对应的函数指针是不一致的

    相关文章

      网友评论

        本文标题:Runtime - load

        本文链接:https://www.haomeiwen.com/subject/gswnmltx.html