iOS runtime的兩次動態(tài)方法決議

基本消息機制流程:iOS 底層 - runtime之objc_msgSend

什么情況下會走兩次動態(tài)方法決議呢 子姜?

以對象方法為例在來到消息動態(tài)轉(zhuǎn)發(fā)階段后會執(zhí)行以下邏輯

  • 未動態(tài)解析過:調(diào)用+ (BOOL)resolveInstanceMethod:(SEL)sel并添加了方法的實現(xiàn),解析成功 底層執(zhí)行 goto retry冰评,標記為已經(jīng)動態(tài)解析 走消息發(fā)送流程:從receiverClass的cache中查找方法(lookUpImpOrForward)這一步開始執(zhí)行

  • 已經(jīng)動態(tài)解析過:直接走消息轉(zhuǎn)發(fā)-->_objc_msgForward_impcache,
    調(diào)用 - (id)forwardingTargetForSelector:(SEL)aSelector 看是否轉(zhuǎn)發(fā)給其他實例來實現(xiàn), 返回空就調(diào)用- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector 看是否返回了方法簽名, 返回方法簽名就再走一次動態(tài)方法決議, 結(jié)果還是沒動態(tài)添加方法就會來到forwardInvocation 到此執(zhí)行結(jié)束

結(jié)論: 當(dāng)我們沒有動態(tài)添加方法實現(xiàn)卻返回了方法簽名時會出發(fā)二次動態(tài)決議,個人猜測蘋果認為你返回了方法簽名 那你大概率也會有對應(yīng)的實現(xiàn) 所以forwardInvocation 之前就再次執(zhí)行了動態(tài)決議, 避免在動態(tài)決議之后 && 方法簽名返回之前你悄悄的給動態(tài)添加了方法的實現(xiàn) 比如你直接就在methodSignatureForSelector中動態(tài)添加了方法的實現(xiàn)

代碼測試


@interface LPersion : NSObject

- (void)test;

@end

+ (BOOL)resolveClassMethod:(SEL)sel {
    NSLog(@"來到--> %s",__func__);
    return [super resolveClassMethod:sel];
}

+ (BOOL)resolveInstanceMethod:(SEL)sel {
    NSLog(@"來到--> %s",__func__);
    return [super resolveInstanceMethod:sel];
}

- (id)forwardingTargetForSelector:(SEL)aSelector {
    NSLog(@"來到--> %s",__func__);
    return [super forwardingTargetForSelector:aSelector];
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    NSLog(@"來到--> %s",__func__);
    return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}

- (void)forwardInvocation:(NSInvocation *)anInvocation {
    NSLog(@"來到--> %s",__func__);
}

打印結(jié)果
來到--> +[LPersion resolveInstanceMethod:]
來到--> -[LPersion forwardingTargetForSelector:]
來到--> -[LPersion methodSignatureForSelector:]
來到--> +[LPersion resolveInstanceMethod:]
來到--> -[LPersion forwardInvocation:]

由于_objc_msgForward_impcache內(nèi)部的實現(xiàn)沒有開源,所以下面用下面方式來探索后續(xù)流程
lookUpImpOrForward--> log_and_fill_cache--> logMessageSend--> instrumentObjcMessageSends

開啟 instrumentObjcMessageSends需要用到extern關(guān)鍵字率拒,這個關(guān)鍵字是可以定義和聲明一個外部函數(shù)(沒有被static修飾的)
這個日志開啟之后溶握,會在 /private/tmp目錄下創(chuàng)建一個 msgSends-xxxx文件


bool logMessageSend(bool isClassMethod,
                    const char *objectsClass,
                    const char *implementingClass,
                    SEL selector)
{
    char    buf[ 1024 ];
    // Create/open the log file
    if (objcMsgLogFD == (-1))
    {
        snprintf (buf, sizeof(buf), "/tmp/msgSends-%d", (int) getpid ());
        objcMsgLogFD = secure_open (buf, O_WRONLY | O_CREAT, geteuid());
        if (objcMsgLogFD < 0) {
            // no log file - disable logging
            objcMsgLogEnabled = false;
            objcMsgLogFD = -1;
            return true;
        }
    }

    // Make the log entry
    snprintf(buf, sizeof(buf), "%c %s %s %s\n",
            isClassMethod ? '+' : '-',
            objectsClass,
            implementingClass,
            sel_getName(selector));

    objcMsgLogLock.lock();
    write (objcMsgLogFD, buf, strlen(buf));
    objcMsgLogLock.unlock();

    // Tell caller to not cache the method
    return false;
}

void instrumentObjcMessageSends(BOOL flag)
{
    bool enable = flag;

    // Shortcut NOP
    if (objcMsgLogEnabled == enable)
        return;

    // If enabling, flush all method caches so we get some traces
    if (enable)
        _objc_flush_caches(Nil);

    // Sync our log file
    if (objcMsgLogFD != -1)
        fsync (objcMsgLogFD);

    objcMsgLogEnabled = enable;
}

示例

extern instrumentObjcMessageSends(BOOL flag);

@interface ViewController ()

@end

@implementation ViewController

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    instrumentObjcMessageSends(true);
    [[LPersion new] test];
    instrumentObjcMessageSends(false);
}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末酌壕,一起剝皮案震驚了整個濱河市晨炕,隨后出現(xiàn)的幾起案子衫画,更是在濱河造成了極大的恐慌毫炉,老刑警劉巖瓮栗,帶你破解...
    沈念sama閱讀 216,651評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡费奸,警方通過查閱死者的電腦和手機弥激,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來愿阐,“玉大人微服,你說我怎么就攤上這事∮Ю” “怎么了以蕴?”我有些...
    開封第一講書人閱讀 162,931評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長辛孵。 經(jīng)常有香客問我丛肮,道長,這世上最難降的妖魔是什么魄缚? 我笑而不...
    開封第一講書人閱讀 58,218評論 1 292
  • 正文 為了忘掉前任宝与,我火速辦了婚禮,結(jié)果婚禮上冶匹,老公的妹妹穿的比我還像新娘习劫。我一直安慰自己,他們只是感情好嚼隘,可當(dāng)我...
    茶點故事閱讀 67,234評論 6 388
  • 文/花漫 我一把揭開白布诽里。 她就那樣靜靜地躺著,像睡著了一般飞蛹。 火紅的嫁衣襯著肌膚如雪须肆。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,198評論 1 299
  • 那天桩皿,我揣著相機與錄音豌汇,去河邊找鬼。 笑死泄隔,一個胖子當(dāng)著我的面吹牛拒贱,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播佛嬉,決...
    沈念sama閱讀 40,084評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼逻澳,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了暖呕?” 一聲冷哼從身側(cè)響起斜做,我...
    開封第一講書人閱讀 38,926評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎湾揽,沒想到半個月后瓤逼,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體笼吟,經(jīng)...
    沈念sama閱讀 45,341評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,563評論 2 333
  • 正文 我和宋清朗相戀三年霸旗,在試婚紗的時候發(fā)現(xiàn)自己被綠了贷帮。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,731評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡诱告,死狀恐怖撵枢,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情精居,我是刑警寧澤锄禽,帶...
    沈念sama閱讀 35,430評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站靴姿,受9級特大地震影響沟绪,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜空猜,卻給世界環(huán)境...
    茶點故事閱讀 41,036評論 3 326
  • 文/蒙蒙 一绽慈、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧辈毯,春花似錦坝疼、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至唁影,卻和暖如春耕陷,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背据沈。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評論 1 269
  • 我被黑心中介騙來泰國打工哟沫, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人锌介。 一個月前我還...
    沈念sama閱讀 47,743評論 2 368
  • 正文 我出身青樓嗜诀,卻偏偏與公主長得像,于是被迫代替她去往敵國和親孔祸。 傳聞我的和親對象是個殘疾皇子隆敢,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,629評論 2 354

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