探索objc_msgSend函數的實現流程

熟悉OC語言的Runtime(運行時)機制以及對象方法調用機制的開發者都知道,全部OC方法調用在編譯時都會轉化爲對C函數objc_msgSend的調用。緩存


objc_msgSend函數是全部OC方法調用的核心引擎,它負責查找真實的類或者對象方法的實現,並去執行這些方法函數。因調用頻率是如此之高,因此要求其內部實現近可能達到最高的性能。這個函數的內部代碼實現是用匯編語言來編寫的。你能夠在runtime源碼下查看各類體系架構下的彙編語言的實現。架構


咱們選擇經常使用的arm64來查看:app


能夠看到,先經過LNilOrTargged判斷對象是不是targetpoint類型或nil,而後對isa進行一系列的寄存器操做,最終獲得LGetIsaDone(isa處理完畢),isa處理完畢後,開始執行CacheLookup NORMAL (在緩存cache_t裏尋找imp,找到的話返回imp,未找到的話返回objc_msgSend_uncached),下面咱們跳入CacheLookup的實現函數


能夠看到有三種狀況:CacheHit、CheckMiss、add。性能

先來查看CacheHit:this


由於咱們傳過來的是NORMAL,因此緊接着執行TailCallCachedImp,回調imp。3d

再來看CheckMiss:cdn


一樣,傳入的是NORMAL,因此會回調objc_msgSend_uncached,接着跳入objc_msgSend_uncached:對象


再跳入MethodTableLookup(能夠猜到是去經過MethodTable方法列表去查找,事實也確實是這樣的,結構體objc_class裏有一個class_rw_t中存儲着方法列表method_array_t (是一張哈希表,對method_t的name和imp進行一一對應),屬性列表property_array_t,協議列表protocol_array_t 等內容),而後去回調FunctionPointer。再來查看MethodTableLookup:blog


也是咔咔一頓看不懂的彙編操做後到了class_lookupMethodAndLoadCache3,再跳入具體實現:


能夠看到,該函數是經過c來實現的(終於告別了難纏的彙編):咱們繼續跳:

IMPlookUpImpOrForward(Classcls,SELsel,idinst, 

                       boolinitialize,boolcache,boolresolver)

{

    IMPimp =nil;

    booltriedResolver =NO;

    runtimeLock.assertUnlocked();

    // Optimistic cache lookup

    if(cache) {

    // 若是cache是YES,則從緩存中查找IMP

        imp =cache_getImp(cls, sel);

        if(imp)returnimp;

    }

    runtimeLock.lock();

    checkIsKnownClass(cls);

    if(!cls->isRealized()) {    判斷類是否已經被建立,若是沒有被建立,則將類實例化

        realizeClass(cls);

    }

    if(initialize  &&  !cls->isInitialized()) {    第一次調用當前類的話,執行initialize的代碼

        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)gotodone;

    // Try this class's method lists.

    {    若是沒有從cache中查找到,則從方法列表中獲取Method

        Method meth = getMethodNoSuper_nolock(cls, sel);

        if(meth) {

            log_and_fill_cache(cls, meth->imp, sel, inst, cls);

            imp = meth->imp;

            gotodone;

        }

    }

    // Try superclass caches and method lists.

    {    若是尚未,就從父類緩存或者方法列表獲取imp

        unsignedattempts =unreasonableClassCount();

        for(ClasscurClass = 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);

                    gotodone;

                }

                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.

            Methodmeth =getMethodNoSuper_nolock(curClass, sel);

            if(meth) {

                log_and_fill_cache(cls, meth->imp, sel, inst, curClass);

                imp = meth->imp;

                gotodone;

            }

        }

    }

    // 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;

        gotoretry;

    }

    // No implementation found, and method resolver didn't help. 

    // Use forwarding.

    imp = (IMP)_objc_msgForward_impcache;    //消息轉發

    cache_fill(cls, sel, imp, inst);

 done:

    runtimeLock.unlock();

    returnimp;

}

lookUpImpOrForward 這個方法裏面篇幅很長裏面介紹瞭如下幾點(如下涉及了消息,動態方法解析,還有消息轉發,咱們這篇文章不作描述。) :

若是cache是YES,則從緩存中查找IMP。這裏也就是說咱們若是以前響應過的,在cache存過,就不須要下面的操做了

判斷類是否已經被建立,若是沒有被建立,則將類實例化

第一次調用當前類的話,執行initialize的代碼

嘗試獲取這個類的緩存 (這裏不少小夥伴就會質疑,爲何還要取一次內存,要知道OC是動態語言,在咱們執行這個獲取imp的時候,外界在開鎖,解鎖的時候是能夠訪問的,動態操做)

若是沒有從cache中查找到,則從方法列表中獲取Method

若是尚未,就遞歸從父類緩存或者方法列表獲取imp

若是沒有找到,則嘗試動態方法解析:_class_resolveMethod

若是沒有IMP被發現,而且動態方法解析也沒有處理,則進入消息轉發階段:_objc_msgForward_impcache

相關文章
相關標籤/搜索