手把手带你探索load底层原理

load的调用规则

  1. 类的load方法在全部父类的load方法调用以后调用
  2. 分类的load方法在当前类的load方法调用以后调用
  3. 分类的load方法的调用顺序和编译顺序有关

探索

和上篇文章手把手带你探索Category底层原理同样,直接打开objc源码,继续来到_objc_init函数数组

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);
}
复制代码
  • 前面提到load_images是dyld初始化加载image方法,因此此次应该探索这个方法
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();
}
复制代码
  • 找到了咱们想要看的load方法,先去探索prepare_load_methods作了什么,等会再去看call_load_methods
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);
    }
}
复制代码
  • getObjc2NonlazyClassListMach-O文件里获取了非懒加载的类列表,而后循环类列表,调用了一个递归方法schedule_class_load
  • 看看schedule_class_load干了什么
  • schedule_class_load是递归调用父类,一直往上找父类并调用,因此这里论证了咱们文章开头的第一条结论:类的load方法在全部父类的load方法调用以后调用
  • 那么再看看add_class_to_loadable_list
  • 回到外面
//针对分类的操做!
    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);
    }
复制代码
  • 能够看到分类也是从Mach-O文件中得到的非懒加载分类,这里刚好验证了分类的load方法的调用顺序和编译顺序有关,编译顺序不一样,load方法调用的顺序不一样markdown

  • add_category_to_loadable_list再看看这个方法作了什么 函数

  • 这里和上面的add_class_to_loadable_list相似,只是换成加载到了分类的全局容器中post

  • 至此,整个prepare_load_methods探索完了,再回头看看call_load_methods作了什么 spa

  • 能够看到,这里是先调用类的load方法,再调用了分类的load方法,正好验证了开头的结论二: 分类的load方法在当前类的load方法调用以后调用ssr

  • call_class_loads就是经过load的SEL调用该方法 code

  • call_category_loads同理 orm

总结