iOS load方法调用机制解析

iOS load方法调用机制解析1. runtime 库入口函数 关于 map_images, load_images, unmap_image 这三个函数我上一篇博客有具体提到, 这里不多赘述了. 直接进去 load_images : 代码中看出这里分为了两步 , 先准备 , 然后调用 . GO 2…

前言:

上篇文章 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 方法的 clsmethod 存储到全局的 loadable_class 结构体中.
    iOS load方法调用机制解析

2.2 分类的 load 方法读取和排序

分类的 load 大体流程和类的基本类似 , 同样先从 Mach-o 读取所有的分类列表 , 然后直接遍历 , 添加分类的 load 方法的 clsmethod 存储到全局的 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

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注