Runtime之objc_msgSend執(zhí)行流程

總覽

Objetive-C的消息發(fā)送河胎,是通過objc_msgSend來實(shí)現(xiàn)的闯袒,具體執(zhí)行過程,主要分三個(gè)階段:

  • 1、消息發(fā)送政敢;
  • 2其徙、動(dòng)態(tài)方法解析
  • 3、消息轉(zhuǎn)發(fā)或重新簽名

消息發(fā)送

Person類有兩個(gè)方法 sayHellosayBye 喷户,Student繼承Person唾那,并重寫 sayHello 方法。

@interface Person : NSObject
-(void)sayHello;
-(void)sayBye;
@end

@interface Student : Person
@end
    
@implementation Person
-(void)sayHello{
    NSLog(@"%s",__func__);
}
-(void)sayBye{
    NSLog(@"%s",__func__);
}
@end
    
@implementation Student
-(void)sayHello{
    NSLog(@"%s",__func__);
}
@end

現(xiàn)在褪尝,通過給Student實(shí)例對(duì)象發(fā)消息,來展示消息的調(diào)用順序

    //1.1 如果接收者類的cache中能找到方法避诽,則直接調(diào)用沙庐。
    //否則從接受者類的方法列表中查找方法,找到后添加到cache中
    Student* student = [[Student alloc] init];
    [student sayHello];
    //1.2 以上兩個(gè)步驟均找不到的時(shí)候佳吞,從superClass的cache中查找轨功,同 1.1
    [student sayBye];

結(jié)果如下:

2019-01-23 19:09:00.825000+0800 runtime_objc_msgSend[14364:5870808] -[Student sayHello]
2019-01-23 19:09:00.825100+0800 runtime_objc_msgSend[14364:5870808] -[Person sayBye]

通過對(duì)結(jié)果的分析,我們得到如下方法查找的順序:

  • 1 接收者首先從接收者類的cache中查找方法
    • 1.1 如果能找到方法容达,直接調(diào)用古涧,結(jié)束
    • 1.2 如果找不到方法,繼續(xù)執(zhí)行2
  • 2 從接收者類的方法列表中查找
    • 2.1 如果找到方法花盐,調(diào)用并將方法添加到接收者類的cache中羡滑,結(jié)束
    • 2.2 如果找不到方法,則從其superClass的cache中查找
    • 遞歸2.1算芯,直到最頂層類柒昏。
  • 3 如果找不到方法职祷,則判斷走以下兩個(gè)步驟
    • 3.1 如果兩個(gè)步驟均不涉及,則直接拋出異常 'unrecognized selector sent to instance'
    • 詳細(xì)步驟參照以下階段
      • 注:每個(gè)階段結(jié)束會(huì)重新進(jìn)入本階段届囚。

動(dòng)態(tài)方法解析

如果消息發(fā)送階段有梆,未找到匹配的方法,則開發(fā)者可以通過重寫NSObject中的以下兩個(gè)方法來對(duì)未匹配的方法進(jìn)行解析意系。

+ (BOOL)resolveClassMethod:(SEL)sel
+ (BOOL)resolveInstanceMethod:(SEL)sel

為了測(cè)試代碼泥耀,我們重寫Student類兜辞,對(duì)其進(jìn)行擴(kuò)展,當(dāng)方法dynamicAnalysisMethod不存在時(shí)夸溶,我們將Student類中方法dynamicAnalysisOther的實(shí)現(xiàn)添加給dynamicAnalysisMethod逸吵。代碼如下

@interface Student : Person
-(void)dynamicAnalysisMethod;
@end
    
@implementation Student
+(BOOL)resolveInstanceMethod:(SEL)sel{
    if (sel == @selector(dynamicAnalysisMethod)) {
        Method method = class_getInstanceMethod([self class], @selector(dynamicAnalysisOther));
        //Adds a new method to a class with a given name and implementation.
        class_addMethod([self class],
                        sel,
                        method_getImplementation(method),
                        method_getTypeEncoding(method));
        return true;
    }
    return [super resolveInstanceMethod:sel];
}

-(void)dynamicAnalysisOther{
    NSLog(@"%s",__func__);
}
@end

此時(shí),我們給Student實(shí)例對(duì)象發(fā)dynamicAnalysisMethod消息缝裁,代碼如下

    Student* student = [[Student alloc] init];
    //針對(duì)類和實(shí)例對(duì)象方法胁塞。
    //2.1重寫NSObject的方法 + (BOOL)resolveClassMethod:(SEL)sel 
    //      或 + (BOOL)resolveInstanceMethod:(SEL)sel
    //2.2在方法中對(duì)方法進(jìn)行動(dòng)態(tài)解析。
    [student dynamicAnalysisMethod];

結(jié)果如下:

2019-01-23 19:09:00.825300+0800 runtime_objc_msgSend[14364:5870808] -[Student dynamicAnalysisOther]

這樣压语,我們實(shí)現(xiàn)了消息的動(dòng)態(tài)解析。

  • 針對(duì)未匹配的方法编检,我們可以通過 class_addMethod 給類添加新的方法和實(shí)現(xiàn)
  • 重新進(jìn)入消息發(fā)送階段

消息轉(zhuǎn)發(fā)

如果在以上兩個(gè)階段均沒有找到相關(guān)方法胎食,此時(shí)就進(jìn)入了消息轉(zhuǎn)發(fā)階段。消息轉(zhuǎn)發(fā)主要有兩個(gè)類別

  • 直接轉(zhuǎn)發(fā)
  • 方法重簽名允懂,轉(zhuǎn)發(fā)

此時(shí)厕怜,我們新建一個(gè)Worker類,詳細(xì)代碼如下:

@interface Worker : NSObject
-(void)sayHello;
-(void)reSignature;
@end
    
@implementation Worker
-(id)forwardingTargetForSelector:(SEL)aSelector{
    if (aSelector == @selector(sayHello)) {
        return [[Student alloc] init];
    }
    return nil;
}

-(NSMethodSignature*)methodSignatureForSelector:(SEL)sel{
    if (sel == @selector(reSignature)) {
       NSMethodSignature* signature = [[[Student alloc]
                                         init]
                                        methodSignatureForSelector:@selector(reSignatureMethod)];
        return signature;
    }
    return [super methodSignatureForSelector:sel];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation{
    NSLog(@"%s",__func__);
    if (anInvocation.selector == @selector(reSignatureMethod)) {
        anInvocation.target = [[Student alloc] init];
        [anInvocation invoke];
        //或者如下形式
        //[anInvocation invokeWithTarget:[[Student alloc] init]];
    }
}
@end

對(duì)Worker類蕾总,有兩個(gè)方法申明sayHelloreSignature 粥航, 但是并不對(duì)其進(jìn)行實(shí)現(xiàn)。此時(shí)我們給Worker的實(shí)例對(duì)象發(fā)送消息生百,如下:

    Worker* worker = [[Worker alloc] init];
    //直接轉(zhuǎn)發(fā)
    //3.1 重寫NSObject的方法 - (id)forwardingTargetForSelector:(SEL)aSelector
    //返回 消息接收者對(duì)象
    [worker sayHello];
    
    //方法重簽名递雀。
    //如果3.1轉(zhuǎn)發(fā)方法返回的是nil。則可以通過重新簽名的方式來實(shí)現(xiàn)
    //4.1 重寫NSObject的類/實(shí)例方法 
    //    - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
    //      或 + (NSMethodSignature *)instanceMethodSignatureForSelector:(SEL)aSelector
    //4.2 在方法中蚀浆,返回新方法的方法簽名
    //4.3 重寫NSObject的方法 - (void)forwardInvocation:(NSInvocation *)anInvocation
    //根據(jù)簽名等信息缀程,對(duì)NSInvocation的target進(jìn)行賦值。然后invoke喚醒
    [worker reSignature];

方法均沒有實(shí)現(xiàn)市俊,我們通過消息轉(zhuǎn)發(fā)杨凑,結(jié)果如下:

2019-01-23 19:09:00.825449+0800 runtime_objc_msgSend[14364:5870808] -[Student sayHello]
2019-01-23 19:09:00.825554+0800 runtime_objc_msgSend[14364:5870808] -[Worker forwardInvocation:]

消息直接轉(zhuǎn)發(fā)

  • 重寫NSObject的 -(id)forwardingTargetForSelector:(SEL)aSelector方法
  • 直接返回接收消息的對(duì)象實(shí)例。

方法重新簽名

  • 通過重寫NSObject的方法

    • - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector 
      + (NSMethodSignature *)instanceMethodSignatureForSelector:(SEL)aSelector 
      
    • 在方法中摆昧,我們針對(duì)reSignature selector進(jìn)行了重新簽名

  • 重寫NSObject方法

    • - (void)forwardInvocation:(NSInvocation *)anInvocation 
      
    • 方法中撩满,對(duì) reSignatureMethod selector的target進(jìn)行了重新賦值

    • 喚醒

  • 進(jìn)入方法發(fā)送階段

Demo

https://github.com/liangtongdev/Demo-runtime_objc_msgSend

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市绅你,隨后出現(xiàn)的幾起案子伺帘,更是在濱河造成了極大的恐慌,老刑警劉巖忌锯,帶你破解...
    沈念sama閱讀 221,635評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件曼追,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡汉规,警方通過查閱死者的電腦和手機(jī)礼殊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門驹吮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人晶伦,你說我怎么就攤上這事碟狞。” “怎么了婚陪?”我有些...
    開封第一講書人閱讀 168,083評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵族沃,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我泌参,道長(zhǎng)脆淹,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,640評(píng)論 1 296
  • 正文 為了忘掉前任沽一,我火速辦了婚禮盖溺,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘铣缠。我一直安慰自己烘嘱,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,640評(píng)論 6 397
  • 文/花漫 我一把揭開白布蝗蛙。 她就那樣靜靜地躺著蝇庭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪捡硅。 梳的紋絲不亂的頭發(fā)上哮内,一...
    開封第一講書人閱讀 52,262評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音壮韭,去河邊找鬼牍蜂。 笑死,一個(gè)胖子當(dāng)著我的面吹牛泰涂,可吹牛的內(nèi)容都是我干的鲫竞。 我是一名探鬼主播,決...
    沈念sama閱讀 40,833評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼逼蒙,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼从绘!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起是牢,我...
    開封第一講書人閱讀 39,736評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤僵井,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后驳棱,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體批什,經(jīng)...
    沈念sama閱讀 46,280評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,369評(píng)論 3 340
  • 正文 我和宋清朗相戀三年社搅,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了驻债。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片乳规。...
    茶點(diǎn)故事閱讀 40,503評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖合呐,靈堂內(nèi)的尸體忽然破棺而出暮的,到底是詐尸還是另有隱情,我是刑警寧澤淌实,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布冻辩,位于F島的核電站,受9級(jí)特大地震影響拆祈,放射性物質(zhì)發(fā)生泄漏恨闪。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,870評(píng)論 3 333
  • 文/蒙蒙 一放坏、第九天 我趴在偏房一處隱蔽的房頂上張望咙咽。 院中可真熱鬧,春花似錦轻姿、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至余素,卻和暖如春豹休,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背桨吊。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評(píng)論 1 272
  • 我被黑心中介騙來泰國打工威根, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人视乐。 一個(gè)月前我還...
    沈念sama閱讀 48,909評(píng)論 3 376
  • 正文 我出身青樓洛搀,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親佑淀。 傳聞我的和親對(duì)象是個(gè)殘疾皇子留美,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,512評(píng)論 2 359

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

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,725評(píng)論 0 9
  • 文中的實(shí)驗(yàn)代碼我放在了這個(gè)項(xiàng)目中伸刃。 以下內(nèi)容是我通過整理[這篇博客] (http://yulingtianxia....
    茗涙閱讀 924評(píng)論 0 6
  • 轉(zhuǎn)載:http://yulingtianxia.com/blog/2014/11/05/objective-c-r...
    F麥子閱讀 735評(píng)論 0 2
  • 本文詳細(xì)整理了 Cocoa 的 Runtime 系統(tǒng)的知識(shí)谎砾,它使得 Objective-C 如虎添翼,具備了靈活的...
    lylaut閱讀 806評(píng)論 0 4
  • Objective-C中有兩個(gè)NSObject捧颅,一個(gè)是NSObject類景图,另一個(gè)是NSObject協(xié)議。而其中NS...
    ScaryMonsterLyn閱讀 766評(píng)論 0 2