美文网首页
iOS-浅谈OC中+load和+initialize方法

iOS-浅谈OC中+load和+initialize方法

作者: 晴天ccc | 来源:发表于2019-05-27 08:44 被阅读0次

    目录

    • +load方法
      ---- 类和分类的+load方法调用顺序
      ---- 不同类间的+load方法调用顺序
      ---- 不同分类间的+load方法调用顺序
      ---- load总结
    • +initialize方法
      ---- +initialize方法调用时机
      ---- +initialize用途
      ---- +initialize和+load对比
      ------------ 调用时机
      ------------ 调用顺序
      ------------ 调用方式

    +load方法

    • 类和分类的+load方法调用顺序

    分析源码:

    • _objc_init(运行时初始化方法)
    • load_images(加载模块方法)
    • call_load_methods(调用load方法)
    • call_class_loads(调用类的load方法)
    • call_category_loads(调用分类的load方法)
    static void call_class_loads(void)
    {
        // 读取所有类
        struct loadable_class *classes = loadable_classes;
    ......
        // 遍历所有类
        for (i = 0; i < used; i++) {
            // 找到class对象中的+load方法指针
            load_method_t load_method = (load_method_t)classes[i].method;
            // 调用+load方法
            (*load_method)(cls, @selector(load));
        }
    ......
    }
    
    static bool call_category_loads(void)
    {
        // 读取所有分类
        struct loadable_category *cats = loadable_categories;
    ......
        // 遍历所有分类
        for (i = 0; i < used; i++) {
            // 找到分类里+load方法指针
            load_method_t load_method = (load_method_t)cats[i].method;
            // 调用+load方法
            (*load_method)(cls, @selector(load));
        }
    ......
    }
    

    由源码观察可知,先调用了call_class_loads方法再调用了call_category_loads方法。
    所以类和分类调用顺序是:
    先调用所有类中的+load方法,再调用所有分类的+load方法。

    • 不同类间的+load方法调用顺序

    分析运行时源码:

    • prepare_load_methods(预加载+load方法)
    • schedule_class_load(定制类的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;
    
        // Ensure superclass-first ordering
        schedule_class_load(cls->getSuperclass());
    
        add_class_to_loadable_list(cls);
        cls->setInfo(RW_LOADED); 
    }
    

    通过分析源码可知:schedule_class_load方法内部有一个递归调用,每次传入参数是cls的superclass,将cls添加到loadable_classes数组中,所以最终结果导致在loadable_classes数组中父类class会排在子类class前面,再结合call_class_loads方法得出:

    • 1、先调用父类中的+load方法,再调用子类中的+load方法。
    • 2、没有继承关系的类,按照编译顺序调用+load方法。
    • 不同分类间的+load方法调用顺序

    void add_category_to_loadable_list(Category cat)
    {
    ......
        loadable_categories[loadable_categories_used].cat = cat;
    ......
    }
    

    结合call_category_loads方法得出:
    分类是按照编译顺序调用+load方法。

    • load总结

    • +load方法会在runtime加载类、分类时调用。
    • 每个类、分类的+load,在程序运行过程中只调用一次。
    • +load方法是根据方法地址直接调用,并不是经过objc_msgSend函数调用

    调用顺序

    • 先调用类的+load
    • 按照编译先后顺序调用(先编译,先调用)
    • 调用子类的+load之前会先调用父类的+load
    • 再调用分类的+load
    • 按照编译先后顺序调用(先编译,先调用)

    +initialize方法

    • +initialize方法调用时机

    查看objc_msgSend源码,因为是汇编语言理解起来困难,所以可以从消息发送的过程入手。
    isa->找到类/元类对象->查找方法->调用

    从查找方法部分入手

    • class_getInstanceMethod(查找对象方法)/class_getClassMethod(查找类方法)
    • lookUpImpOrForward(查找方法实现)
    IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior)
    {
    ......    
        // 如果没有初始化,需要调用initialize方法
        if ((behavior & LOOKUP_INITIALIZE)  &&  !cls->isInitialized()) {
            initializeNonMetaClass (_class_getNonMetaClass(cls, inst));
        }
    ......
    }
    

    initializeNonMetaClass:

    void initializeNonMetaClass(Class cls)
    {
    ......
        // 拿到父类
        supercls = cls->getSuperclass();
        // 父类如果没有初始化,先初始化父类
        if (supercls  &&  !supercls->isInitialized()) {
            initializeNonMetaClass(supercls);
        }
    ......
        callInitialize(cls);
    ......
    }
    
    // 调用cls的initialize方法
    void callInitialize(Class cls)
    {
        ((void(*)(Class, SEL))objc_msgSend)(cls, @selector(initialize));
        asm("");
    }
    

    通过查看源码可知,在objc_msgSend(消息发送)过程中,会先调用类的+initialize方法。
    观察到initializeNonMetaClass方法内部有递归调用,递归参数是superclass,所以调用子类+initialize前,会先判断父类是否已经初始化了,如果没有会先调用父类的+initialize方法。

    总结:

    • 1、+initialize方法会在类第一次接收到消息时调用。
    • 2、调用顺序
      先调用父类的+initialize,再调用子类的+initialize(先初始化父类,再初始化子类,每个类只会初始化1次)
    • +initialize用途

    initialize用途就是在第一次使用这个类的时候做一些事情。

    • +initialize和+load对比

    • 调用时机

    +load在runtime加载类、分类时调用(只会调用一次)
    +initialize是类第一次接收到消息时调用,每一个类只会initialize一次(如果子类没有实现initialize方法,父类的initialize方法可能会被调用多次)

    • 调用顺序

    +load

    • 1、先调用类的+load
      先编译的类,优先调用+load
      调用子类+load之前,会先调用父类的+load
    • 2、再调用分类
      先编译的分类,优先调用+load

    +initialize

    • 1、先初始化父类
    • 2、再初始化子类(可能最终调用的是父类的+initialize方法)
    • 调用方式

    +load是通过方法指针调用,+initialize是通过objc_msgSend方式调用,所以+initialize会有以下特点:

    • 1、如果子类没有实现+initialize方法,会调用父类的+initialize(所以父类的+initialize可能会被调用多次,第一次为自己初始化时调用,其他为子类消息发送调用)
    • 2、如果分类实现了+initialize,会覆盖主类的+initialize。

    相关文章

      网友评论

          本文标题:iOS-浅谈OC中+load和+initialize方法

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