initialize

294 阅读3分钟

大家经常谈论load和initialize,

其实他们之间真的一点关系都没有,只是因为用的比较少,所以被对比了起来

在介绍应用启动_objc_init时,介绍了load方法的调用时机,在main函数之前,且顺序为父类->当前类->分类

而initialize的调用时机在main函数之后,且为该类第一次发送消息时才调用,下面就探索一下

系统在查找方法是会调用lookUpImpOrForward方法,查找imp是否存在,如果不存在从cache或者rw的methods中找,方法查找流程这里就先细讲解了,直接定位到_class_initialize方法

_class_initialize当initialize没有被调用时会被调用起来,如下所示

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
}

_class_initialize部分实现如下,先看前面,提到了superclass,当superclass的_class_initialize方法没有调用的时候,会递归似的先调用父类的_class_initialize方法,最后当有没执行initialize方法,即reallyInitialize为真,然后才开始执行callInitialize方法,回调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;
        }
    }
    
    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;
        }
        
        // 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: thread %p: calling +[%s initialize]",
                         pthread_self(), cls->nameForLogging());
        }

        // Exceptions: A +initialize call that throws an exception 
        // is deemed to be a complete and successful +initialize.
        //
        // Only __OBJC2__ adds these handlers. !__OBJC2__ has a
        // bootstrapping problem of this versus CF's call to
        // objc_exception_set_functions().
#if __OBJC2__
        @try
#endif
        {
            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()) {
        // We couldn't set INITIALIZING because INITIALIZING was already set.
        // If this thread set it earlier, continue normally.
        // If some other thread set it, block until initialize is done.
        // It's ok if INITIALIZING changes to INITIALIZED while we're here, 
        //   because we safely check for INITIALIZED inside the lock 
        //   before blocking.
        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()) {
        // Set CLS_INITIALIZING failed because someone else already 
        //   initialized the class. Continue normally.
        // NOTE this check must come AFTER the ISINITIALIZING case.
        // Otherwise: Another thread is initializing this class. ISINITIALIZED 
        //   is false. Skip this clause. Then the other thread finishes 
        //   initialization and sets INITIALIZING=no and INITIALIZED=yes. 
        //   Skip the ISINITIALIZING clause. Die horribly.
        return;
    }
    
    else {
        // We shouldn't be here. 
        _objc_fatal("thread-safe class init in objc runtime is buggy!");
    }
}

callInitialize方法实现如下,其实就是调用了系统的objc_msgSend,直接发送消息执行

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

因此,得出结论initialize的调用时机在main函数之后,且调用时机为第一次调用方法时调用,调用顺序为父类->当前类,并且只调用一次

分类和当前类调用顺序,可以方法查找流程进行分析,如果你看到了前面的文章大致,可以猜想出结果了:

父类的分类 -> 当前类分类

注意:注意,如果当前类未实现该方法,则会调用父类方法,则父类方法可能会被多次调用,需注意;如果分类方法不存在就,会调用当前类的方法;如果分类方法存在,只会调用分类,不会再调用当前类的同方法

(分类在方法列表最前面,系统调用方法时会先找到分类调用)