美文网首页
十二、+ load方法分析

十二、+ load方法分析

作者: KB_MORE | 来源:发表于2020-09-24 18:11 被阅读0次

    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 而是直接指针调用

    相关文章

      网友评论

          本文标题:十二、+ load方法分析

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