消息查找流程

iOS中方法的查找分為兩部分:

  • 快速查找,在緩存中直接找到發(fā)送的消息
  • 慢速查找,在緩存中如果沒找到就到了慢速查找,先找當前類,再找父類以及父類的父類,如果找到就執(zhí)行,如果找不到就執(zhí)行動態(tài)方法解析和消息轉發(fā)

1.代碼分析方法查找流程

三者關系如圖所示:


圖片.png

子類中對象方法的層層調用

LGStudent *student = [[LGStudent alloc] init];
        // 對象方法
        // 自己有 - 返回自己
        [student sayHello];
        // 自己沒有 - 老爸有 -
        [student sayNB];
        // 自己沒有 - 老爸沒有 - NSObject
        [student sayMaster];
        // 自己沒有 - 老爸沒有 - NSObject 沒有
        // unrecognized selector sent to instance 0x103000450
       [student performSelector:@selector(saySomething)];

打印結果:


圖片.png

結論:只要是繼承關系,子類可以調用父類中的所有對象方法,如果一直追究到NSObject中也沒有的對象方法saySomething,則會報出一個最經(jīng)典最常見的錯誤信息:unrecognized selector sent to instance 0x100562f70

子類中類方法的層層調用

圖片.png

打印結果:


圖片.png

結論:同上,只不過這里論證的是類方法。即,幾個類中不存在sayLove方法,所以會報經(jīng)典錯誤。

子類調用NSObject中的對象方法

圖片.png

結論:完全遵守isa的走位流程圖(http://www.itdecent.cn/p/cf18f3cc1306)

2.源碼分析消息查找流程

主要涉及方法:_class_lookupMethodAndLoadCache3

圖片.png
IMP lookUpImpOrForward(Class cls, SEL sel, id inst, bool initialize, bool cache, bool resolver) 

參數(shù)解讀

  • cls 當前類
  • sel 當前方法的名字
  • inst 當前類對象
  • initialize 是否初始化
  • cache 是否緩存
  • resolver 是否在執(zhí)行動態(tài)方法解析

2.如果緩存命中 直接返回imp


圖片.png
if (cache) {
        imp = cache_getImp(cls, sel);
        if (imp) return imp;
    }

3.realizeClass 方法實現(xiàn)

static Class realizeClass(Class cls)
{
   runtimeLock.assertLocked();

   const class_ro_t *ro;  // 方法列表 屬性列表 等的存儲結構題
   class_rw_t *rw; // 存儲 class_ro_t的結構題
   Class supercls; // 父類
   Class metacls; // 元類
   bool isMeta; // 當前類是否是元類
   ro = (const class_ro_t *)cls->data();  // 給ro賦值
   isMeta = ro->flags & RO_META; // 是否是元類判斷
   supercls = realizeClass(remapClass(cls->superclass)); // 初始化父類
   metacls = realizeClass(remapClass(cls->ISA())); // 初始化元類
   cls->superclass = supercls; 找到當前類的 父類
   cls->initClassIsa(metacls); 初始化當前類的元類
   if (supercls) { // 如果有父類就把當前類關聯(lián)到父類的子類列表中
       addSubclass(supercls, cls);
   } else {
       addRootClass(cls);
   }

4.如果類沒有初始化就執(zhí)行初始化 _class_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
   }

5.開始找方法和存儲緩存

{
       Method meth = getMethodNoSuper_nolock(cls, sel); // 一個for循環(huán) 從方法列表中找方法
       if (meth) { // 如果找到方法 開始緩存 
           log_and_fill_cache(cls, meth->imp, sel, inst, cls); // 這里會調用 cache_fill 上一篇已經(jīng)寫過
           imp = meth->imp;
           goto done; //  找到直接返回
       }
   }

6.如果當前類沒找到,就查找父類以及父類的父類,并且執(zhí)行緩存,如果找到直接返回

for (Class curClass = cls->superclass;
            curClass != nil;
            curClass = curClass->superclass)

7.如果以上的過程都沒有找到方法,那么就執(zhí)行動態(tài)方法解析,也就是 if (resolver && !triedResolver) {這個判斷體中的內容}

8. _class_resolveMethod 方法實現(xiàn)

void _class_resolveMethod(Class cls, SEL sel, id inst)
{
   // 如果不是元類 說明是類 此時類中已經(jīng)沒有方法 直接執(zhí)行  _class_resolveInstanceMethod
   if (! cls->isMetaClass()) {
       // try [cls resolveInstanceMethod:sel]

       _class_resolveInstanceMethod(cls, sel, inst); //  執(zhí)行 + (BOOL)resolveInstanceMethod:(SEL)sel方法
   }
   else { 
       // try [nonMetaClass resolveClassMethod:sel]
       // and [cls resolveInstanceMethod:sel]
       _class_resolveClassMethod(cls, sel, inst);
       if (!lookUpImpOrNil(cls, sel, inst, 
                           NO/*initialize*/, YES/*cache*/, NO/*resolver*/)) 
       {
           _class_resolveInstanceMethod(cls, sel, inst); // 
       }
   }
}

上述代碼當是元類的時候: cls 就是元類 inst 類對象 這時會做兩次嘗試 resolveClassMethod _class_resolveInstanceMethod

9.最后動態(tài)方法解析會走到 _class_resolveInstanceMethod, 如果類沒有實現(xiàn)resolveInstanceMethod就直接返回 如果類實現(xiàn)了 resolveInstanceMethod 就發(fā)送 SEL_resolveInstanceMethod消息

static void _class_resolveInstanceMethod(Class cls, SEL sel, id inst)
{
    if (! lookUpImpOrNil(cls->ISA(), SEL_resolveInstanceMethod, cls, 
                         NO/*initialize*/, YES/*cache*/, NO/*resolver*/)) 
    {
        // Resolver not implemented.
        return;
    }

    BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
    bool resolved = msg(cls, SEL_resolveInstanceMethod, sel);

    // Cache the result (good or bad) so the resolver doesn't fire next time.
    // +resolveInstanceMethod adds to self a.k.a. cls
    IMP imp = lookUpImpOrNil(cls, sel, inst, 
                             NO/*initialize*/, YES/*cache*/, NO/*resolver*/);
}


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

相關閱讀更多精彩內容

友情鏈接更多精彩內容