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

1,278 阅读2分钟

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方法调用的顺序不同

  • add_category_to_loadable_list再看看这个方法做了什么

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

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

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

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

  • call_category_loads同理

总结