十二、+ load方法分析

2020-09-24  本文已影响0人  KB_MORE

load方法加载顺序

图片.png

类: LGPerson
子类: LGTeacher
分类1: LGPerson+EatFirst.h 先创建
分类2:LGPerson+DrinkSecond 后创建

调用顺序

-> 子类 -> 分类(后创建) -> 分类(先创建)

注意: 这里分类的load调用顺序是和编译顺序有关的(Compile Sources在前面,先调用那个)和创建顺序无关, 不过我们一般不会刻意的去调整文件在Compile Sources的顺序

图片.png

探究

首先我们先在类中重写下load方法, 然后打个断点, 运行项目,可以看到堆栈的调用情况

堆栈调用.png
可以看到在 load调用之前调用了load_images, 我们在objc的源码中可以找到load_images的实现,
其实load_images是在_objc_init函数中调用的, 下面看下几个函数的实现

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();
    runtime_init();
    exception_init();
    cache_init();
    _imp_implementationWithBlock_init();

    _dyld_objc_notify_register(&map_images, load_images, unmap_image);

#if __OBJC2__
    didCallDyldNotifyRegister = true;
#endif
}

load_images

void
load_images(const char *path __unused, const struct mach_header *mh)
{
    if (!didInitialAttachCategories && didCallDyldNotifyRegister) {
        didInitialAttachCategories = true;
        loadAllCategories();
    }

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

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

call_load_methods中其实我们就可以看到类和分类的调用顺序了,类先于分类
再看call_class_loads的实现

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, @selector(load));
    }
    
    // Destroy the detached list.
    if (classes) free(classes);
}

断点调试,


图片.png

会看到classes里面包含了LGPersonLGTeacher, 如果再多创建子类, 这里也会有变化
到这里就可以得出结论,

调用父类的load方法, 然后是子类的load方法
并且load方法并不是使用的消息发送 objc_msgSend 而是直接指针调用

上一篇下一篇

猜你喜欢

热点阅读