Initialize方法底层实现

initialize方法

  • 调用时机:initialize在类第一次接收到消息时调用,也就是objc_msgSend(),当一个类在查找方法的时候,会先判断当前类是否初始化,如果没有初始化就会去调用initialize方法。
  • 调用顺序:先调用父类的+initialize,再调用类放入initialize。
  • 调用次数:有可能是多次
  • 分类中的实现:只执行分类的调用
  • 类在调用initalize时,使用的是objc_msgSend消息机制调用
  • 作用:初始化操作

可以自己写代码举例证明,这里只是通过runtime源码,去了解一下。
首先,在runtime源码中,我们创建了一个Person类,在Person类的initalize方法中进行打印,并且加上断点:


屏幕快照 2019-09-22 下午9.33.42.png
  • 直接运行,当然是没有任何输出的,因为上面有提到initialize的调用时机,initialize在类第一次接收到消息时调用
  • main.m 文件中,调用Person 的class方法:


    屏幕快照 2019-09-22 下午9.35.18.png

此时,我们会看到走到了断点处:


屏幕快照 2019-09-22 下午9.36.41.png

从上图中我们可以看到,main函数执行之后的流程是:

  • _objc_msgSend_uncached
  • _class_lookupMethodAndLoadCache3
  • lookUpImpOrForward
  • _class_initialize
  • callInitialize
  • [Person initialize]

我们直接从第3步看就行了,下面是lookUpImpOrForward的方法代码:

IMP lookUpImpOrForward(Class cls, SEL sel, id inst, 
                       bool initialize, bool cache, bool resolver)
{
    ... // 判断这个类是否初始化
    if (initialize  &&  !cls->isInitialized()) {
        runtimeLock.unlock();
        _class_initialize (_class_getNonMetaClass(cls, inst));
        runtimeLock.lock();
        // 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
    }
...
}

cls->isInitialized() 其实就是一个标记这个类是否初始化的标志:

bool isInitialized() {
        return getMeta()->data()->flags & RW_INITIALIZED;
    }

此时我们继续看 _class_initialize 这个方法:

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

    Class supercls;
    bool reallyInitialize = NO;

    // 先调用父类的,再调用子类的
    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;
        }
    }
    
    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);

        if (MultithreadedForkChild) {
            // LOL JK we don't really call +initialize methods after fork().
            performForkChildInitialize(cls, supercls);
            return;
        }
        

        if (PrintInitializing) {
            _objc_inform("INITIALIZE: thread %p: calling +[%s initialize]",
                         pthread_self(), cls->nameForLogging());
        }
#if __OBJC2__
        @try
#endif
        {
// 调用 cls 的initalize 方法
            callInitialize(cls);

            if (PrintInitializing) {
                _objc_inform("INITIALIZE: thread %p: finished +[%s initialize]",
                             pthread_self(), cls->nameForLogging());
            }
        }
#if __OBJC2__
        @catch (...) {
            if (PrintInitializing) {
                _objc_inform("INITIALIZE: thread %p: +[%s initialize] "
                             "threw an exception",
                             pthread_self(), cls->nameForLogging());
            }
            @throw;
        }
        @finally
#endif
        {
            // Done initializing.
            lockAndFinishInitializing(cls, supercls);
        }
        return;
    }
    
    else if (cls->isInitializing()) {
        if (_thisThreadIsInitializingClass(cls)) {
            return;
        } else if (!MultithreadedForkChild) {
            waitForInitializeToComplete(cls);
            return;
        } else {
            // We're on the child side of fork(), facing a class that
            // was initializing by some other thread when fork() was called.
            _setThisThreadIsInitializingClass(cls);
            performForkChildInitialize(cls, supercls);
        }
    }
    
    else if (cls->isInitialized()) {
        return;
    }
    
    else {
        _objc_fatal("thread-safe class init in objc runtime is buggy!");
    }
}

继续看callInitialize 方法:

void callInitialize(Class cls)
{
    // 消息查找流程
    ((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
    asm("");
}

所以到此非常明了,从源码中我们可以看到

  • 先调用父类的initialize方法,再调用子类的initalize方法,由于initalize方法会走消息查找流程,所以当分类中也实现了initialize方法之后,只会执行分类的initalize方法。
  • 如果本类没有实现initialize方法,父类实现了initialize方法,则多个子类初始化时会多次调用父类的initialize方法,但是本质上只有第一次initialize方法是初始化父类,后面几个initialize都是方法的调用,即子类没有实现,通过superclass到父类里查找。

+initialize和+load的一个很大区别是,+initialize是通过objc_msgSend进行调用的,所以有以下特点:

1、如果子类没有实现+initialize方法,会调用父类的+initialize(所以父类的+initialize方法可能会被调用多次)
2、如果分类实现了+initialize,会覆盖类本身的+initialize调用。

你可能感兴趣的:(Initialize方法底层实现)