initialize

一、初探

  • load函數(shù)地址調(diào)用!!! 不經(jīng)過消息查找流程,也就是發(fā)送消息(objc_msgSend:)
  • initialize當前對象接收到第一條消息的時候會調(diào)用!!!
//當前對象接收到第一條消息的時候會調(diào)用!!!
+ (void)initialize {
    NSLog(@"%@,%s",self,__func__);
}

//函數(shù)地址調(diào)用!!! 不經(jīng)過消息查找流程,也就是發(fā)送消息(objc_msgSend:)
+ (void)load{
//    NSLog(@"load");//不會執(zhí)行initialize
    NSLog(@"%@,%s",self,__func__);//當前 self 接受到了消息,會執(zhí)行initialize
}

二、initialize源碼分析

1、objc4-750源碼中搜索lookUpImpOrForward

extern IMP lookUpImpOrForward(Class, SEL, id obj, bool initialize, bool cache, bool resolver);

2、點擊lookUpImpOrForward

IMP lookUpImpOrForward(Class cls, SEL sel, id inst, 
                       bool initialize, bool cache, bool resolver)
{
    IMP imp = nil;
    bool triedResolver = NO;

    runtimeLock.assertUnlocked();

    // Optimistic cache lookup
    // 查找緩存!??!
    if (cache) {
        //匯編代碼的方式實現(xiàn)的!
        imp = cache_getImp(cls, sel);
        if (imp) return imp;
    }

    // runtimeLock is held during isRealized and isInitialized checking
    // to prevent races against concurrent realization.

    // runtimeLock is held during method search to make
    // method-lookup + cache-fill atomic with respect to method addition.
    // Otherwise, a category could be added but ignored indefinitely because
    // the cache was re-filled with the old value after the cache flush on
    // behalf of the category.

    runtimeLock.lock();
    checkIsKnownClass(cls);

    if (!cls->isRealized()) {
        realizeClass(cls);
    }

    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
    }
    
 retry:    
    runtimeLock.assertLocked();

    // Try this class's cache.

    imp = cache_getImp(cls, sel);
    if (imp) goto done;

    // Try this class's method lists.
    {
        //Method(SEL IMP)
        Method meth = getMethodNoSuper_nolock(cls, sel);
        if (meth) {
            log_and_fill_cache(cls, meth->imp, sel, inst, cls);
            imp = meth->imp;
            goto done;
        }
    }

    // Try superclass caches and method lists.
    {
        unsigned attempts = unreasonableClassCount();
        for (Class curClass = cls->superclass;
             curClass != nil;
             curClass = curClass->superclass)
        {
            // Halt if there is a cycle in the superclass chain.
            if (--attempts == 0) {
                _objc_fatal("Memory corruption in class list.");
            }
            
            // Superclass cache.
            imp = cache_getImp(curClass, sel);
            if (imp) {
                if (imp != (IMP)_objc_msgForward_impcache) {
                    // Found the method in a superclass. Cache it in this class.
                    log_and_fill_cache(cls, imp, sel, inst, curClass);
                    goto done;
                }
                else {
                    // Found a forward:: entry in a superclass.
                    // Stop searching, but don't cache yet; call method 
                    // resolver for this class first.
                    break;
                }
            }
            
            // Superclass method list.
            Method meth = getMethodNoSuper_nolock(curClass, sel);
            if (meth) {
                log_and_fill_cache(cls, meth->imp, sel, inst, curClass);
                imp = meth->imp;
                goto done;
            }
        }
    }

    // No implementation found. Try method resolver once.
    if (resolver  &&  !triedResolver) {
        runtimeLock.unlock();
        _class_resolveMethod(cls, sel, inst);
        runtimeLock.lock();
        // Don't cache the result; we don't hold the lock so it may have 
        // changed already. Re-do the search from scratch instead.
        triedResolver = YES;
        goto retry;
    }

    // No implementation found, and method resolver didn't help. 
    // Use forwarding.
    //_objc_msgForward
    imp = (IMP)_objc_msgForward_impcache;
    cache_fill(cls, sel, imp, inst);

 done:
    runtimeLock.unlock();

    return imp;
}

3、點擊_class_initialize

  • 遞歸調(diào)用,先調(diào)用父類,再調(diào)用子類
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!");
    }
}

4、點擊callInitialize

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

三、initialize總結(jié)

  • 調(diào)用時機:收到第一條消息
  • 調(diào)用順序:先父類、再子類
  • 調(diào)用次數(shù):多次(父類實現(xiàn)了initialize,子類沒實現(xiàn),初始化子類,父類會調(diào)用兩次),load方法只會調(diào)用一次
父類實現(xiàn)了initialize,子類沒實現(xiàn)
2019-09-09 10:46:58.553175+0800 LGTest[9621:1967424] LGPerson,+[LGPerson initialize]
2019-09-09 10:46:58.553226+0800 LGTest[9621:1967424] LGStudent,+[LGPerson initialize]

父類實現(xiàn)了initialize,子類也實現(xiàn)了initialize
2019-09-09 10:47:25.426857+0800 LGTest[9638:1969466] LGPerson,+[LGPerson initialize]
2019-09-09 10:47:25.426949+0800 LGTest[9638:1969466] LGStudent,+[LGStudent initialize]
  • 分類中的實現(xiàn),只執(zhí)行分類的調(diào)用(分類的頭文件都不需要導入)
  • 作用:初始化常量

總結(jié):
情況一:父類實現(xiàn)initialize
1、調(diào)用父類,執(zhí)行父類的initialize
2、調(diào)用子類,執(zhí)行父類的initialize
情況二:子類實現(xiàn)initialize
1、調(diào)用父類,不執(zhí)行
2、調(diào)用子類,執(zhí)行子類的initialize
情況三:分類實現(xiàn)initialize
1、調(diào)用父類,執(zhí)行分類的initialize
2、調(diào)用子類,執(zhí)行分類的initialize
情況四:父類實現(xiàn)initialize、分類實現(xiàn)initialize
1、調(diào)用父類,執(zhí)行分類的initialize
2、調(diào)用子類,執(zhí)行分類的initialize
情況五:父類實現(xiàn)initialize、子類實現(xiàn)initialize
1、調(diào)用父類,執(zhí)行父類的initialize
2、調(diào)用子類,執(zhí)行父類的initialize、執(zhí)行子類的initialize
情況六:分類實現(xiàn)initialize、子類實現(xiàn)initialize
1、調(diào)用父類,執(zhí)行分類的initialize
2、調(diào)用子類,執(zhí)行分類的initialize、執(zhí)行子類的initialize
情況七:父類實現(xiàn)initialize、分類實現(xiàn)initialize、子類實現(xiàn)initialize
1、調(diào)用父類,執(zhí)行分類的initialize
2、調(diào)用子類,執(zhí)行分類的initialize、執(zhí)行子類的initialize

分類的作用相當于覆蓋類,就是如果分類和類都實現(xiàn)了某個方法,會調(diào)用分類的。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容