美文网首页OC底层原理
load 和 initialize方法调用

load 和 initialize方法调用

作者: 只写Bug程序猿 | 来源:发表于2020-02-28 15:24 被阅读0次

我们前边说,程序启动肯定会来到

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

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();
}

这里重点代码在prepare_load_methodscall_load_methods

1. prepare_load_methods
void prepare_load_methods(const headerType *mhdr)
{
    size_t count, i;

    runtimeLock.assertLocked();
//获取所有非懒加载类,这里实现了load方法所以加载懒加载类
    classref_t *classlist = 
        _getObjc2NonlazyClassList(mhdr, &count);
    for (i = 0; i < count; i++) {
//循环遍历非懒加载类
        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
        if (cls->isSwiftStable()) {
            _objc_fatal("Swift class extensions and categories on Swift "
                        "classes are not allowed to have +load methods");
        }
        realizeClassWithoutSwift(cls);
        assert(cls->ISA()->isRealized());
//添加分类到非懒加载list中
        add_category_to_loadable_list(cat);
    }
}
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); 
}
void add_class_to_loadable_list(Class cls)
{
    IMP method;

    loadMethodLock.assertLocked();
//拿到load方法method
    method = cls->getLoadMethod();
    if (!method) return;  // Don't bother if cls has no +load method
    
    if (PrintLoading) {
        _objc_inform("LOAD: class '%s' scheduled for +load", 
                     cls->nameForLogging());
    }
    //判断是否需要扩容
    if (loadable_classes_used == loadable_classes_allocated) {
        loadable_classes_allocated = loadable_classes_allocated*2 + 16;
        loadable_classes = (struct loadable_class *)
            realloc(loadable_classes,
                              loadable_classes_allocated *
                              sizeof(struct loadable_class));
    }
    //将cls给数组的属性 给model赋值
    loadable_classes[loadable_classes_used].cls = cls;
//将method给数组的method
    loadable_classes[loadable_classes_used].method = method;
    loadable_classes_used++;
}
struct loadable_class {
    Class cls;  // may be nil
    IMP method;
};

  • 拿到非懒加载类classlist,然后循环遍历调用schedule_class_load
  • schedule_class_load,递归调用找到自己和父类的load方法然后add_class_to_loadable_list加到数组中去
  • 然后判断是否需要扩容,这里说下为什么 + 16. loadable_class是一个结构体,cls8字节,imp8字节所以加16
2. 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) {
            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方法是先调用主类,然后调用分类
static void call_class_loads(void)
{
    int i;
    
    // Detach current loadable list.
    struct loadable_class *classes = loadable_classes;
    int used = loadable_classes_used;
    loadable_classes = nil;
    loadable_classes_allocated = 0;
    loadable_classes_used = 0;
    
    // Call all +loads for the detached list.
    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);
}

这里是具体的调用方法

  • 循环遍历拿到非懒加载类
  • 得到imp并且强转成load_method_t,然后直接调用函数
    call_category_loads和call_class_loads相似

initialize

initialize方法调用时机参照类和分类的加载

总结:

  • load方法是在load_images里调用的
  • load方法调用,主类优先级优于分类,也就是说先调用主类的load然后执行分类的load方法
  • 普通方法调用和load方法相反,如果主类和分类都有同名的方法,则先调用分类的方法.因为attachmethodlist是先加载类的方法,然后将分类的方法插入到了前边,所以先调用分类方法,这里会造成一个分类方法覆盖主类方法的假象,其实是没有覆盖的,只不过是在方法查找的时候先找到分类然后查找结束.

相关文章

网友评论

    本文标题:load 和 initialize方法调用

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