iOS-動態(tài)方法決議 & 消息轉(zhuǎn)發(fā)

iOS-慢速方法查找iOS-快速方法查找中我們分別提到了objc_msgSend的快速查找和慢速查找袍暴,如果經(jīng)歷這兩步仍未找到該方法的imp會怎么樣呢佛呻?
Apple給了我們兩次補(bǔ)救的機(jī)會,
動態(tài)方法決議:慢速查找流程未找到后,會執(zhí)行一次動態(tài)方法決議
消息轉(zhuǎn)發(fā):如果動態(tài)方法決議仍然沒有找到實現(xiàn),則進(jìn)行消息轉(zhuǎn)發(fā)
如果這兩次機(jī)會都沒有做任何操作,就會報我們?nèi)粘i_發(fā)中常見的方法未實現(xiàn)的崩潰報錯——unrecognized selector sent to
我們來看看是怎么從慢速查找進(jìn)入到第一個機(jī)會——動態(tài)方法決議的吧跨扮。

動態(tài)方法決議入口

康康該方法的源碼

static NEVER_INLINE IMP
resolveMethod_locked(id inst, SEL sel, Class cls, int behavior)
{
    runtimeLock.assertLocked();
    ASSERT(cls->isRealized());

    runtimeLock.unlock();
    //對象 -- 類
    if (! cls->isMetaClass()) { //類不是元類,調(diào)用對象的解析方法
        // try [cls resolveInstanceMethod:sel]
        resolveInstanceMethod(inst, sel, cls);
    } 
    else {//如果是元類验毡,調(diào)用類的解析方法衡创, 類 -- 元類
        // try [nonMetaClass resolveClassMethod:sel]
        // and [cls resolveInstanceMethod:sel]
        resolveClassMethod(inst, sel, cls);
        //為什么要有這行代碼? -- 類方法在元類中是對象方法晶通,所以還是需要查詢元類中對象方法的動態(tài)方法決議
        if (!lookUpImpOrNil(inst, sel, cls)) { //如果沒有找到或者為空璃氢,在元類的對象方法解析方法中查找
            resolveInstanceMethod(inst, sel, cls);
        }
    }

    // chances are that calling the resolver have populated the cache
    // so attempt using it
    //如果方法解析中將其實現(xiàn)指向其他方法,則繼續(xù)走方法查找流程
    return lookUpImpOrForward(inst, sel, cls, behavior | LOOKUP_CACHE);
}

先看看對象方法的動態(tài)決議源碼

static void resolveInstanceMethod(id inst, SEL sel, Class cls)
{
    runtimeLock.assertUnlocked();
    ASSERT(cls->isRealized());
    SEL resolve_sel = @selector(resolveInstanceMethod:);

    if (!lookUpImpOrNilTryCache(cls, resolve_sel, cls->ISA(/*authenticated*/true))) {//查找resolveInstanceMethod是否實現(xiàn)狮辽,其實源碼中NSObject實現(xiàn)了該方法一也。
        // Resolver not implemented.
        return;
    }
   //消息轉(zhuǎn)發(fā)巢寡,執(zhí)行resolveInstanceMethod方法
    BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
    bool resolved = msg(cls, resolve_sel, 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 = lookUpImpOrNilTryCache(inst, sel, cls);
    //感覺是在打日志
    if (resolved  &&  PrintResolving) {
        if (imp) {
            _objc_inform("RESOLVE: method %c[%s %s] "
                         "dynamically resolved to %p", 
                         cls->isMetaClass() ? '+' : '-', 
                         cls->nameForLogging(), sel_getName(sel), imp);
        }
        else {
            // Method resolver didn't add anything?
            _objc_inform("RESOLVE: +[%s resolveInstanceMethod:%s] returned YES"
                         ", but no new implementation of %c[%s %s] was found",
                         cls->nameForLogging(), sel_getName(sel), 
                         cls->isMetaClass() ? '+' : '-', 
                         cls->nameForLogging(), sel_getName(sel));
        }
    }
}

我們來代碼驗證一下,給LGPerson.h添加如下代碼

@interface LGPerson : NSObject

+ (void)say1;//只聲明椰苟,未實現(xiàn)
+ (void)say4;

- (void)say2;
- (void)say3;//只聲明抑月,未實現(xiàn)
@end

LGPerson.m添加如下代碼

@implementation LGPerson

//- (void)say1
//{
//  NSLog(@"%s",__func__);
//}
- (void)say2
{
  NSLog(@"%s",__func__);
}
//- (void)say3
//{
//  NSLog(@"%s",__func__);
//}
+ (void)say4
{
  NSLog(@"%s",__func__);
}


+ (BOOL)resolveInstanceMethod:(SEL)sel{
    if (sel == @selector(say3)) {
        NSLog(@"%@ 來了", NSStringFromSelector(sel));
    }
    return [super resolveInstanceMethod:sel];
}
@end

main中調(diào)用say3,執(zhí)行結(jié)果如下:

image.png

可以看到resolveInstanceMethod執(zhí)行了兩次,why舆蝴?
-第一次的“來了”是在查找say3方法時會進(jìn)入動態(tài)方法決議
-第二次“來了”是在慢速轉(zhuǎn)發(fā)流程中調(diào)用了CoreFoundation框架中的NSObject(NSObject) methodSignatureForSelector:后爪幻,會再次進(jìn)入動態(tài)決議
可通過lldbbt命令看看堆棧信息驗證
image.png

如果想抓住這個防止崩潰的機(jī)會,可以修改resolveInstanceMethod代碼如下

+ (BOOL)resolveInstanceMethod:(SEL)sel{
    if (sel == @selector(say3)) {
        NSLog(@"%@ 來了", NSStringFromSelector(sel));
        //獲取say2方法的imp
        IMP imp = class_getMethodImplementation(self, @selector(say2));
        //獲取say2的實例方法
        Method sayMethod  = class_getInstanceMethod(self, @selector(say2));
        //獲取say2的簽名
        const char *type = method_getTypeEncoding(sayMethod);
        //將sel的實現(xiàn)指向say2
        return class_addMethod(self, sel, imp, type);
    }
//
    return [super resolveInstanceMethod:sel];
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末须误,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子仇轻,更是在濱河造成了極大的恐慌京痢,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,348評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件篷店,死亡現(xiàn)場離奇詭異祭椰,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)疲陕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,122評論 2 385
  • 文/潘曉璐 我一進(jìn)店門方淤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蹄殃,你說我怎么就攤上這事携茂。” “怎么了诅岩?”我有些...
    開封第一講書人閱讀 156,936評論 0 347
  • 文/不壞的土叔 我叫張陵讳苦,是天一觀的道長。 經(jīng)常有香客問我吩谦,道長鸳谜,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,427評論 1 283
  • 正文 為了忘掉前任式廷,我火速辦了婚禮咐扭,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘滑废。我一直安慰自己蝗肪,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,467評論 6 385
  • 文/花漫 我一把揭開白布策严。 她就那樣靜靜地躺著穗慕,像睡著了一般。 火紅的嫁衣襯著肌膚如雪妻导。 梳的紋絲不亂的頭發(fā)上逛绵,一...
    開封第一講書人閱讀 49,785評論 1 290
  • 那天怀各,我揣著相機(jī)與錄音,去河邊找鬼术浪。 笑死瓢对,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的胰苏。 我是一名探鬼主播硕蛹,決...
    沈念sama閱讀 38,931評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼硕并!你這毒婦竟也來了法焰?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,696評論 0 266
  • 序言:老撾萬榮一對情侶失蹤倔毙,失蹤者是張志新(化名)和其女友劉穎埃仪,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體陕赃,經(jīng)...
    沈念sama閱讀 44,141評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡卵蛉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,483評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了么库。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片傻丝。...
    茶點故事閱讀 38,625評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖诉儒,靈堂內(nèi)的尸體忽然破棺而出葡缰,到底是詐尸還是另有隱情,我是刑警寧澤忱反,帶...
    沈念sama閱讀 34,291評論 4 329
  • 正文 年R本政府宣布运准,位于F島的核電站,受9級特大地震影響缭受,放射性物質(zhì)發(fā)生泄漏胁澳。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,892評論 3 312
  • 文/蒙蒙 一米者、第九天 我趴在偏房一處隱蔽的房頂上張望韭畸。 院中可真熱鬧,春花似錦蔓搞、人聲如沸胰丁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽锦庸。三九已至,卻和暖如春蒲祈,著一層夾襖步出監(jiān)牢的瞬間甘萧,已是汗流浹背萝嘁。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留扬卷,地道東北人牙言。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像怪得,于是被迫代替她去往敵國和親咱枉。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,492評論 2 348

推薦閱讀更多精彩內(nèi)容