美文网首页面试好文
load和initialize的区别

load和initialize的区别

作者: 磊Se | 来源:发表于2021-03-17 16:43 被阅读0次

调用机制

load方法的本质:直接执行函数指针

load方法是在运行时被执行的(main函数之前),其调用栈如下

_ load_images  
//加载类和类别的load方法
└── load_images_nolock 
    //执行所有load方法
    └── call_load_methods

而在load_images_nolock方法中,则调用了prepare_load_methods,其执行了两个方法:

_ prepare_load_methods  
//先将需要执行 load 的 class 添加到一个全局列表里 (loadable_class)
└── schedule_class_load 
    //然后将需要执行 load 的 category 添加到另一个全局列表里(loadable_category)
    └── add_category_to_loadable_list 

而在shedule_class_load方法中,确保先将父类添加到列表中。

static void schedule_class_load(class_t *cls)
{
    assert(isRealized(cls));  // _read_images should realize
    if (cls->data->flags & RW_LOADED) return;
    //确保先将父类添加到全局列表里 (loadable_class)
    class_t *supercls = getSuperclass(cls);
    if (supercls) schedule_class_load(supercls);
    //再将当前类添加到全局列表里 (loadable_class)
    add_class_to_loadable_list((Class)cls);
    changeInfo(cls, RW_LOADED, 0); 
}

然后再执行call_load_methods方法时

_ call_load_methods  
//先遍历 loadable_classes 列表中的类,执行 load  方法。
└── call_class_loads 
    //然后再遍历 loadable_category 列表中的分类 ,执行 load  方法。
    └── call_category_loads 

而在call_class_loads中,执行load方法的代码为

// Call all +loads for the detached list.
    for (i = 0; i < used; i++) {
        Class cls = classes[i].cls;
        IMP load_method = classes[i].method;
        if (!cls) continue; 
        if (PrintLoading) {
            _objc_inform("LOAD: +[%s load]\n", _class_getName(cls));
        }
        (*load_method) ((id) cls, SEL_load);
    }

由以上可知,load方法,其实就是直接执行函数指针,不会执行消息发送objc_msgSend那一套流程。子类、分类的load方法不会覆盖父类的load方法。

initialize方法的本质

在类、或者子类,接收到第一条消息之前被执行(如初始化)
initialize方法最终通过objc_msgSend来执行
initialize方法在main函数之后调用
如果一直没有使用类,则initialize方法不会被调用
如果子类没有实现initialize方法,则会调用父类的initialize方法

源码分析:

__private_extern__ void _class_initialize(Class cls)
{
    Class supercls;
    BOOL reallyInitialize = NO;

    // Get the real class from the metaclass. The superclass chain 
    // hangs off the real class only.
    cls = _class_getNonMetaClass(cls);

    // Make sure super is done initializing BEFORE beginning to initialize cls.
    // See note about deadlock above.
    supercls = _class_getSuperclass(cls);
    if (supercls  &&  !_class_isInitialized(supercls)) {
        _class_initialize(supercls);
    }
    
    // Try to atomically set CLS_INITIALIZING.
    monitor_enter(&classInitLock);
    if (!_class_isInitialized(cls) && !_class_isInitializing(cls)) {
        _class_setInitializing(cls);
        reallyInitialize = YES;
    }
    monitor_exit(&classInitLock);
    
    if (reallyInitialize) {
        // We successfully set the CLS_INITIALIZING bit. Initialize the class.
        
        // Record that we're initializing this class so we can message it.
        _setThisThreadIsInitializingClass(cls);
        
        // Send the +initialize message.
        // Note that +initialize is sent to the superclass (again) if 
        // this class doesn't implement +initialize. 2157218
        if (PrintInitializing) {
            _objc_inform("INITIALIZE: calling +[%s initialize]",
                         _class_getName(cls));
        }

        ((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);

        if (PrintInitializing) {
            _objc_inform("INITIALIZE: finished +[%s initialize]",
                         _class_getName(cls));
        }        
        
        // Done initializing. 
        ......
}

优先执行父类的initialize方法;通过_class_getSupercass取出父类,递归调用父类的initialize方法;initialize方法最终通过objc_msgSend来执行的。

执行顺序

load

  • 先调用类的load,再调用分类的load
  • 先编译的类,优先调用load,调用子类的load之前,会先调用父类的load
  • 先编译的分类,优先调用load,顺序和Compile Sources中顺序一致

场景1 :子类、父类、分类都实现load方法,调用情况
答:SuperClass->SubClass->CategoryClass
场景2 :子类、父类、分类中子类不实现load方法,调用情况
答:SuperClass->CategoryClass
场景3 :子类、父类、分类1、分类2都实现load方法,调用情况
答:SuperClass->SubClass->Category1Class->Category2Class

initialize

  • 父类先于子类执行;(同load方法)
  • 子类未实现,则会调用父类的initialize方法;
  • 分类实现了initialize方法,则会覆盖类中的initialize方法(同category);
  • 存在多个分类,依赖Compile Sources中的顺序,执行最后一个分类的initialize方法(同category);

场景1 :子类、父类都实现initialize方法,调用情况
答:SuperClass->SubClass
场景2 :子类、父类中子类不实现initialize方法,调用情况
答:SuperClass->SuperClass(子类未实现,则会调用父类的initialize,导致父类调用多次)
场景3:子类、父类、子类分类都实现initialize方法,调用情况
答:SuperClass->CategoryClass(category中initialize方法覆盖其本类)
场景4:子类、父类、父类分类1、父类分类2都实现initialize方法,调用情况
答:CategoryClass->SubClass(category中initialize方法根据Compile Sources排序执行最后一个)

使用场景

  • load通常用于Method Swizzle
  • initialize可以用于初始化全局变量或静态变量;
    注意:load和initialize方法内部使用了锁,因此他们是线程安全的。使用时避免阻塞线程,不要使用线程锁。

面试题

1、runtime中的交换方法在initialize中实现会有什么问题?
答:initialize方法可能被其分类中的initialize方法覆盖,导致无法调用。

相关文章

网友评论

    本文标题:load和initialize的区别

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