阅读 739

探索 +initialize 底层调用机制 并与+load 比较

前言

在上一篇文章中(手把手带你探索load底层原理)探索了load的调用机制,本文是探索一下initialize的调用机制,并比较一下异同点。 +initialize其实在平时开发中用的较少,一般用来初始化常量,不过面试中常会问到它与+load的区别。

initialize调用规则

  1. +initialize 方法是在类或它的子类收到第一条消息之前被调用的,这里所指的消息包括实例方法和类方法的调用。也就是说 +initialize 方法是以懒加载的方式被调用的,如果程序一直没有给某个类或它的子类发送消息,那么这个类的 +initialize 方法是永远不会被调用的。
  2. 当子类没有实现+initialize或者子类在+initialize中显式的调用了[super initialize],那么父类的+initialize方法会被调用多次。
  3. 在分类实现+initialize,会只执行分类的+initialize
  4. 类的+initialize方法在所有父类的+initialize方法调用之后调用

验证调用规则

这次是在objc源码中直接调试代码,在我提供的源码里新建一个target, 如下图

接下来创建Person类,实现loadinitialize方法,编译运行我们刚才创建的target

运行结果只执行了load方法,并没有执行initialize方法,所以能够得到结论:load在文件被加载时就执行了,initialize不主动调用

对于本文开头的几个结论,可以在这里都简单用代码验证一遍,我这里就不再去一一验证了,直接开始探索源码。

探索

我在load里打印了一下self,发现initialize被调用了,那么看看断点处的initialize

发现这里其实就进入到了runtime的消息查找流程,这里直接来到第三步,这里判断是否类已经初始化,没有初始化就做初始化操作。所以在这里可以验证开头的结论一,它是收到类的第一条消息后才调用的。

看看_class_initialize函数

void _class_initialize(Class cls)
{
    assert(!cls->isMetaClass());

    Class supercls;
    bool reallyInitialize = NO;

    // Make sure super is done initializing BEFORE beginning to initialize cls.
    // See note about deadlock above.
    supercls = cls->superclass;
    if (supercls  &&  !supercls->isInitialized()) {
        _class_initialize(supercls);
    }
    
    // Try to atomically set CLS_INITIALIZING.
    {
        monitor_locker_t lock(classInitLock);
        if (!cls->isInitialized() && !cls->isInitializing()) {
            cls->setInitializing();
            reallyInitialize = YES;
        }
    }
    //其余代码省略
    callInitialize(cls);
复制代码
  • 这里有一个标识符reallyInitialize,在cls->setInitializing()(给类的isa设置RW_INITIALIZING标识),设置完后改为true

  • 这里有拿到 supercls,然后判断是否存在和是否初始化完成,未初始化则调用自己本身这个函数_class_initialize,其实就是一个递归,用来确保所有父类的initialize已经执行完,再执行当前类,这里和之前探索load的时候的递归调用父类load的方法原理相同。所以在这里可以验证开头的结论四:类的+initialize方法在所有父类的+initialize方法调用之后调用。

void callInitialize(Class cls)
{
    ((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
    asm("");
}
复制代码
  • 最后会调用callInitialize, 使用了发送消息 objc_msgSend 的方式对 +initialize 方法进行调用。也就是说 +initialize 方法的调用与普通方法的调用是一样的,走的都是发送消息的流程。换言之,如果子类没有实现 +initialize 方法,那么继承自父类的实现会被调用;所以有了结论三:如果一个类的分类实现了 +initialize 方法,那么就会对这个类中的实现造成覆盖。
  • 因此,这里验证了开头的结论二:如果一个子类没有实现 +initialize 方法,那么父类的实现是会被执行多次的

总结

关注下面的标签,发现更多相似文章
评论