美文网首页
iOS原理(四)----+load,+initialize

iOS原理(四)----+load,+initialize

作者: 会笑的Even | 来源:发表于2018-12-27 00:49 被阅读0次

    iOS原理(四)----+load,+initialize

    +load

    +load方法会在runtime加载类、分类时调用,每个类、分类的+load,在程序运行过程中只调用一次.

    创建Animal及其Run,Sleep分类,分别实现+load方法

    @interface Animal : NSObject
    
    @end
    
    @implementation Animal
    
    + (void)load {
        NSLog(@"Animal load");
    }
    
    @end
    
    @interface Animal (Run)
    
    @end
    
    @implementation Animal (Run)
    
    + (void)load {
        NSLog(@"Animal (Run) load");
    }
    
    @end
    
    @interface Animal (Sleep)
    
    @end
    
    @implementation Animal (Sleep)
    
    + (void)load {
        NSLog(@"Animal (Sleep) load");
    }
    
    @end
    

    编译顺序如下:

    Snip20181110_9.png

    根据前面已知Category原理,应该打印Sleep分类的+load方法,运行程序打印如下:

    Snip20181110_10.png

    先调用Animal类的+load,再调用Run分类的+load,最后才调用Sleep分类的+load.继续寻找源码,发现如下:

    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);
    
        // 获取所有+load方法
        {
            rwlock_writer_t lock2(runtimeLock);
            prepare_load_methods((const headerType *)mh);
        }
    
        // 调用所有+load方法
        call_load_methods();
    }
    // 获取所有的+load方法
    void prepare_load_methods(const headerType *mhdr)
    {
        size_t count, I;
    
        runtimeLock.assertWriting();
        
        // 所有的类
        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
            realizeClass(cls);
            assert(cls->ISA()->isRealized());
            add_category_to_loadable_list(cat);
        }
    }
    // 处理所有类的+load方法
    static void schedule_class_load(Class cls)
    {
        if (!cls) return;
        assert(cls->isRealized());  // _read_images should realize
    
        if (cls->data()->flags & RW_LOADED) return;
    
        // 确保父类在顺序的前面
        schedule_class_load(cls->superclass);
        // 添加到loadable_list中
        add_class_to_loadable_list(cls);
        cls->setInfo(RW_LOADED); 
    }
    
    // 调用+load方法
    void call_load_methods(void)
    {
        static bool loading = NO;
        bool more_categories;
    
        loadMethodLock.assertLocked();
    
        if (loading) return;
        loading = YES;
    
        void *pool = objc_autoreleasePoolPush();
    
        do {
            // 1.调用类的+load方法
            while (loadable_classes_used > 0) {
                call_class_loads();
            }
    
            // 2.调用分类的+load方法
            more_categories = call_category_loads();
    
        } while (loadable_classes_used > 0  ||  more_categories);
    
        objc_autoreleasePoolPop(pool);
    
        loading = NO;
    }
    
    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;
        
        // 调用所有的类+load方法
        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);
    }
    

    从上面的代码得知:

    • 1.先调用类的+load方法,再调用分类的+load方法按编译顺序调用.
    • 2.类的+load方法,先调用父类的+load.
    • 3.类的+load方法,按照编译编译顺序调用.

    前面的打印证明了第一点,接下来证明第2,第3点.

    新建Cat类继承Animal,

    @interface Cat : Animal
    
    @end
    
    @implementation Cat
    
    + (void)load {
        NSLog(@"Cat load");
    }
    
    @end
    

    打印如下


    Snip20181110_2.png

    第2点得到证明.

    新建Dog类类继承Animal,

    @interface Dog : Animal
    
    @end
    
    @implementation Dog
    
    + (void)load {
        NSLog(@"Dog load");
    }
    @end
    

    编译如图:


    Snip20181110_11.png

    打印如下:


    Snip20181110_12.png

    第3点得到证明.

    +initialize方法

    +initialize方法会在类第一次接收到消息时调用.
    注释掉前面所有类的+load方法,实现+initialize方法(简单打印改方法).
    编译文件和顺序如图:

    Snip20181110_13.png

    使用CatDog两个类.

    Snip20181110_14.png

    打印如下:

    Snip20181110_15.png

    可以知道先调用Animal``sleep分类的+initialize,再调用Cat+initialize,再调用Dog+initialize.我们可以从苹果源码中寻找答案,
    lookUpImpOrForward()有一段实现如下;

        // 需要初始化和该类并没有初始化时
        if (initialize  &&  !cls->isInitialized()) {
            runtimeLock.unlockRead();
            _class_initialize (_class_getNonMetaClass(cls, inst));
            runtimeLock.read();
            // If sel == initialize, _class_initialize will send +initialize and 
            // then the messenger will send +initialize again after this 
            // procedure finishes. Of course, if this is not being called 
            // from the messenger then it won't happen. 2778172
        }
    

    _class_initialize()有一段代码实现如下:

        // superclass存在且superclass没有初始化时
        if (supercls  &&  !supercls->isInitialized()) {
            _class_initialize(supercls);
        }
        
                {
                callInitialize(cls);
    
                if (PrintInitializing) {
                    _objc_inform("INITIALIZE: thread %p: finished +[%s initialize]",
                                 pthread_self(), cls->nameForLogging());
                }
            }
    

    callInitialize()实现如下:

    void callInitialize(Class cls)
    {
        ((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
        asm("");
    }
    

    可以看出+initialize是通过objc_msgSend进行调用的.得出结论:

    • 先调用父类的+initialize,再调用子类的+initialize.
    • 如果分类实现了就调用分类的+initialize,按照后编译先调用原则.

    相关文章

      网友评论

          本文标题:iOS原理(四)----+load,+initialize

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