系統(tǒng)底層源碼分析(18)——objc_msgSend

當(dāng)我們調(diào)用方法時(shí),進(jìn)入?yún)R編模式可以發(fā)現(xiàn),底層其實(shí)會(huì)調(diào)用objc_msgSend進(jìn)行快速查找,這個(gè)方法是用匯編寫的,詳請(qǐng)我們就不看了,就主要看流程:

1.對(duì)接受者進(jìn)行判空處理:檢查這個(gè)selector是不是要忽略。檢測(cè)這個(gè)selectortarget是不是nil,OC允許我們對(duì)一個(gè)nil對(duì)象執(zhí)行任何方法不會(huì)崩潰,因?yàn)檫\(yùn)行時(shí)會(huì)被忽略掉。
2.進(jìn)行taggedPoint等異常處理
3.獲取到接受者isa,對(duì)isa & mask獲取到class
4.通過(guò)對(duì)classisa進(jìn)行指針偏移,獲取到cache_t
5.通過(guò)對(duì)cache_tkey & mask獲取到下標(biāo),查找到對(duì)應(yīng)的bucket,獲取到其中的IMP
6.如果上述快速查找流程沒(méi)有找到IMP,就走到__objc_msgSend_uncached中的MethodTableLookup開始慢速查找(最終調(diào)用_class_lookupMethodAndLoadCache3
{\large\text{作者:低調(diào)的默認(rèn)名 鏈接:https://juejin.cn/post/6844904033484816391 來(lái)源:掘金 著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處。}}

(詳情可以看參考文章

  • 源碼
  1. objc4-750源碼探究:
IMP _class_lookupMethodAndLoadCache3(id obj, SEL sel, Class cls)
{        
    return lookUpImpOrForward(cls, sel, obj, 
                              YES/*initialize*/, NO/*cache*/, YES/*resolver*/);
}
IMP lookUpImpOrForward(Class cls, SEL sel, id inst, 
                       bool initialize, bool cache, bool resolver)
{
    ...
    if (!cls->isRealized()) {
        realizeClass(cls);//準(zhǔn)備-父類
    }
    ...
 retry:    
    runtimeLock.assertLocked();

    // Try this class's cache.

    imp = cache_getImp(cls, sel);//從緩存獲取imp
    if (imp) goto done;//找到返回
    //緩存中沒(méi)有找到就去查找方法列表
    // Try this class's method lists.
    {//局部作用域,可避免名字沖突
        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) { ... }
            
            // Superclass cache.
            imp = cache_getImp(curClass, sel);//從父類緩存獲取imp
            if (imp) {
                if (imp != (IMP)_objc_msgForward_impcache) {
                    log_and_fill_cache(cls, imp, sel, inst, curClass);
                    goto done;
                }
                else {
                    break;
                }
            }
            //緩存中沒(méi)有找到就去查找父類方法列表
            // 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);//對(duì)找不到的方法進(jìn)行處理
        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.

    imp = (IMP)_objc_msgForward_impcache;//報(bào)錯(cuò)
    cache_fill(cls, sel, imp, inst);

 done:
    runtimeLock.unlock();

    return imp;
}
  1. 如果從類(objc_class)的緩存(cache)中找到方法就返回,沒(méi)有就從方法列表中查找:
static method_t *
getMethodNoSuper_nolock(Class cls, SEL sel)
{
    runtimeLock.assertLocked();

    assert(cls->isRealized());
    // fixme nil cls? 
    // fixme nil sel?

    for (auto mlists = cls->data()->methods.beginLists(), 
              end = cls->data()->methods.endLists(); 
         mlists != end;
         ++mlists)
    {
        method_t *m = search_method_list(*mlists, sel);
        if (m) return m;
    }

    return nil;
}
static method_t *search_method_list(const method_list_t *mlist, SEL sel)
{
    int methodListIsFixedUp = mlist->isFixedUp();
    int methodListHasExpectedSize = mlist->entsize() == sizeof(method_t);
    
    if (__builtin_expect(methodListIsFixedUp && methodListHasExpectedSize, 1)) {
        return findMethodInSortedMethodList(sel, mlist);//二分法查找方法
    } else { ... }
    ...
    return nil;
}
  1. 找到方法后對(duì)其進(jìn)行緩存:
static void
log_and_fill_cache(Class cls, IMP imp, SEL sel, id receiver, Class implementer)
{
    ...
    cache_fill (cls, sel, imp, receiver);
}
void cache_fill(Class cls, SEL sel, IMP imp, id receiver)
{
#if !DEBUG_TASK_THREADS
    mutex_locker_t lock(cacheUpdateLock);
    cache_fill_nolock(cls, sel, imp, receiver);
#else
    _collecting_in_critical();
    return;
#endif
}

最后調(diào)用cache_fill_nolock進(jìn)行緩存。(詳情看上篇:類的cache

  1. 如果在當(dāng)前類沒(méi)找到,就遞歸往上找,流程與當(dāng)前類一樣。找到后進(jìn)行返回(goto done)。
  1. 如果真的找不到,就會(huì)進(jìn)入特殊處理并再次查找(goto retry),如果還失敗就進(jìn)行報(bào)錯(cuò)。(詳情看下篇:動(dòng)態(tài)方法決議&消息轉(zhuǎn)發(fā)

快速查找 objc_msgSend
慢速查找 lookUpImpOrForward

  • 驗(yàn)證
@interface NSObject (Test)
- (void)obj_say;
+ (void)obj_cls_say;
@end
@interface Person : NSObject
- (void)p_say;
+ (void)p_cls_say;
@end
@interface Student : Person
- (void)s_say;
+ (void)s_cls_say;
@end
//調(diào)用
Student *student = [[Student alloc] init];
[student s_say];//Student
[student p_say];//Student->Person
[student obj_say];//Student->Person->NSObject

[Student s_cls_say];//Student元類
[Student p_cls_say];//Student元類->Person元類
[Student obj_cls_say];//Student元類->Person元類->NSObject元類

[Student obj_say];//Student元類->Person元類->NSObject元類(根元類)->NSObject

前面6個(gè)方法都執(zhí)行,很正常,但是最后一個(gè)方法也能執(zhí)行。因?yàn)轭惙椒ù嬖谟谠愔?,遞歸往上查找方法時(shí)便找到NSObject元類的父類,也就是NSObjectNSObject該類中保存的是對(duì)象方法,便找到了obj_say

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

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

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