iOS 消息轉(zhuǎn)發(fā)機制(VN的逃生之路)

故事背景:在德瑪西亞的戰(zhàn)場上圣絮,硝煙彌漫,紫色方英雄薇恩正在河道處戲弄一只毫無攻擊性的螃蟹聘殖,絲毫沒有感覺到附近的殺氣晨雳。突然行瑞,從草叢中冒出敵方四員大將奸腺,只聽其中一名怒吼:“德瑪西亞!!!”......

描述此故事前,先附上一張故事的整體流程圖:

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

這里有一個類ADCHero, 有四個方法血久,分別是skillQ, skillW, skillE, skillR, 但是skillR方法沒有實現(xiàn)突照。
在ADCHero.h 文件

@interface ADCHero : NSObject

// Q技能
- (void)skillQ;

// W技能
- (void)skillW;

// E技能
- (void)skillE;

// R技能
- (void)skillR;

@end

在ADCHero.m文件

@implementation ADCHero

- (void)skillQ {
    NSLog(@"ADC 發(fā)起了Q技能");
}

- (void)skillW {
    NSLog(@"ADC 發(fā)起了W技能");
}

- (void)skillE {
    NSLog(@"ADC 發(fā)起了E技能");
}

@end 

創(chuàng)建一個薇恩對象

 ADCHero *vn = [[ADCHero alloc] init];

薇恩在面臨生命危險的時候,準備逃跑氧吐,于是準備開啟大招R 進入隱身狀態(tài)讹蘑。

調(diào)用方法

[vn skillR];// 直接運行程序,報錯: unrecognized selector sent to instance 0x170006e00 程序崩潰筑舅,因為找不到方法實現(xiàn)座慰。

在Objective-C中對象調(diào)用方法,實際上是給對象發(fā)送消息翠拣,在底層會調(diào)用objc_msgSend方法

//  第一個參數(shù)是消息的接收者; 第二個參數(shù)是要調(diào)用的方法名; 后面的參數(shù)依次是調(diào)用的方法中的參數(shù)版仔。
objc_msgSend(receiver, selector, arg1, arg2, …) :

結(jié)局一: 可是薇恩忘了自己沒有加R的技能點,使用不出R技能误墓,于是在敵方四人的圍毆下蛮粮,壯烈犧牲。(程序崩潰)

打印信息:

程序崩潰

為了不讓薇恩這么快就死了谜慌,Objective-C做了一些處理(消息轉(zhuǎn)發(fā))

在薇恩沒法使用R技能時然想,先詢問薇恩,能否使用其它的技能逃跑欣范。薇恩想:“我沒有R技能变泄,那我直接閃現(xiàn)逃跑吧”。情形如下恼琼。

此時會調(diào)用ADCHero類的類方法resolveInstanceMethod, 在這個方法中動態(tài)添加其它的方法妨蛹。

+ (BOOL)resolveInstanceMethod:(SEL)sel {
    NSLog(@"%s", __func__);
    NSString *selectorString = NSStringFromSelector(sel);
    if ([selectorString isEqualToString:@"skillR"]) { // 如果方法名是skillR
        class_addMethod(self, sel, (IMP)skillFlash, "@:"); // 動態(tài)添加方法skillFlash, 參數(shù)一: 消息接收者驳癌;參數(shù)二: 調(diào)用的方法名滑燃;參數(shù)三:方法對應(yīng)的實現(xiàn)地址;參數(shù)四: 類型編碼颓鲜。
        return YES; // 
    }

    return [super resolveInstanceMethod:sel];
}

void skillFlash() {
    NSLog(@"閃現(xiàn)");
}

打印信息:

結(jié)果

薇恩使用閃現(xiàn)極限逃生表窘。典予。。 但是乐严,故事的情節(jié)有變化瘤袖,有的時候閃現(xiàn)是處于CD狀態(tài),也無法使用昂验,不能夠閃現(xiàn)逃走捂敌,vn在想:"要不找隊友支援吧!",于是把消息發(fā)給了隊友日女既琴。正巧日女從附近焦急地趕過來占婉。

在代碼中,將resolveInstanceMethod的方法內(nèi)部更改一下甫恩。并重寫forwardingTargetForSelector方法, forwardingTargetForSelector方法內(nèi)部逆济,創(chuàng)建了一個輔助英雄日女,該類中有方法skillR 磺箕,并且已經(jīng)實現(xiàn)奖慌。
+ (BOOL)resolveInstanceMethod:(SEL)sel {
NSLog(@"%s", func);
return NO;
}

- (id)forwardingTargetForSelector:(SEL)aSelector {
    NSLog(@"%s", __func__);
    NSString *selectorString = NSStringFromSelector(aSelector);
    if ([selectorString isEqualToString:@"skillR"]) {
        AsistantHero *rinv = [[AsistantHero alloc] init];
        return rinv;
    }

    // 如果隊友不在身邊
    return [super forwardingTargetForSelector:aSelector];
}

AsistantHero類.h .m文件如下:

 // AsistantHero.h文件
@interface AsistantHero : NSObject

// Q技能
- (void)skillQ;

// W技能
- (void)skillW;

// E技能
- (void)skillE;

// R技能
- (void)skillR;

@end

// AsistantHero.m 文件
@implementation AsistantHero
- (void)skillQ {
    NSLog(@"SUP 發(fā)起了Q技能");
}

- (void)skillW {
    NSLog(@"SUP 發(fā)起了W技能");
}

- (void)skillE {
    NSLog(@"SUP 發(fā)起了E技能");
}

- (void)skillR {
    NSLog(@"SUP 發(fā)起了R技能 保護了ADC");
}

@end

運行程序,打印信息如下:

打印信息

日女使用R技能減速了敵方四人松靡,并救援薇恩 順利逃走简僧。。雕欺。岛马。。阅茶。但是蛛枚,故事的情節(jié)又有變化,由于在下路對線的時候ADC薇恩和輔助日女產(chǎn)生了分歧脸哀,導致日女心里不太爽蹦浦,于是日女不大想救薇恩。結(jié)果薇恩又死了撞蜂。

在代碼中盲镶,將forwardingTargetForSelector方法內(nèi)部修改一下:

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

運行程序,打印信息如下:

打印信息

然而蝌诡,故事情節(jié)又有轉(zhuǎn)機溉贿,薇恩告訴日女:“如果你不救我,我就掛機浦旱!”,日女考慮到這把是自己的晉級賽宇色,于是心想:“就救他一次吧,反正救人一命勝造七級浮屠⌒洌”

在代碼中, 重寫methodSignatureForSelector方法和forwardInvocation方法.

// 完整的消息轉(zhuǎn)發(fā)機制
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    NSLog(@"%s", __func__);
    NSString *selectorString = NSStringFromSelector(aSelector);
    if ([selectorString isEqualToString:@"skillR"]) {
        NSMethodSignature *signature = [NSMethodSignature signatureWithObjCTypes:"v@:"];
        return signature;
    }

    return nil;
}

- (void)forwardInvocation:(NSInvocation *)anInvocation {
    NSLog(@"%s", __func__);
    AsistantHero *rinv = [[AsistantHero alloc] init];
    if ([rinv respondsToSelector:[anInvocation selector]]) {
        [anInvocation invokeWithTarget:rinv];
    } else {
        [super forwardInvocation:anInvocation];
    }
}

運行程序例隆,結(jié)果如下:

打印結(jié)果

最后日女抓住了最后一次機會,救下了薇恩抢蚀,薇恩為了表達感激之情镀层,讓他的王者表哥帶日女打贏了晉級賽。

總結(jié)一下皿曲,在上面的故事中唱逢,薇恩面對敵方四人埋伏,自己又沒有R技能逃亡屋休。如何使用iOS的消息轉(zhuǎn)發(fā)機制一步一步的驚險逃脫坞古。當他調(diào)用 [vn skillR]方法時,究竟做了哪些事博投。
第一階段绸贡,看自己能不能在接受到這個消息時盯蝴,使用閃現(xiàn)逃跑毅哗,如果不能,進入到第二個階段捧挺。
第二階段虑绵,看有沒有輔助在身旁替自己接受這個消息,如果有就把消息傳遞給輔助闽烙,讓輔助救援他翅睛。如果沒有,則進入第三個階段黑竞。
第三階段捕发,把消息封裝起來,告訴輔助很魂,給他最后一次機會扎酷,讓他設(shè)法處理。

對應(yīng)與消息轉(zhuǎn)發(fā)機制遏匆,iOS消息轉(zhuǎn)發(fā)分為三大階段:

  • 第一階段法挨,先征詢消息接收者所屬的類,看其是否能動態(tài)添加方法幅聘,以處理當前這個無法響應(yīng)的selector, 及動態(tài)方法解析凡纳。如果運行期系統(tǒng) 第一階段執(zhí)行結(jié)束,接收者就無法再以動態(tài)新增方法的手段來響應(yīng)消息帝蒿,進入第二階段荐糜。
  • 第二階段,看看有沒有其它對象(備用接收者)能處理此消息。如果有暴氏,運行期系統(tǒng)會把消息發(fā)給那個對象丛版,轉(zhuǎn)發(fā)過程結(jié)束;如果沒有偏序,則啟動完整的消息轉(zhuǎn)發(fā)機制页畦。
  • 第三階段,完整的消息轉(zhuǎn)發(fā)機制研儒。運行期系統(tǒng)會把與消息有關(guān)的全部細節(jié)都封裝到NSInvocation對象中豫缨,再給接收者最后一次機會,令其設(shè)法解決當前還未處理的問題端朵。

參考資料:
https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Introduction/Introduction.html
https://hit-alibaba.github.io/interview/iOS/ObjC-Basic/Runtime.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末好芭,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子冲呢,更是在濱河造成了極大的恐慌舍败,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件敬拓,死亡現(xiàn)場離奇詭異邻薯,居然都是意外死亡,警方通過查閱死者的電腦和手機乘凸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進店門厕诡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人营勤,你說我怎么就攤上這事灵嫌。” “怎么了葛作?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵寿羞,是天一觀的道長。 經(jīng)常有香客問我赂蠢,道長绪穆,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任客年,我火速辦了婚禮霞幅,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘量瓜。我一直安慰自己司恳,他們只是感情好,可當我...
    茶點故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布绍傲。 她就那樣靜靜地躺著扔傅,像睡著了一般耍共。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上猎塞,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天试读,我揣著相機與錄音,去河邊找鬼荠耽。 笑死钩骇,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的铝量。 我是一名探鬼主播倘屹,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼慢叨!你這毒婦竟也來了纽匙?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤拍谐,失蹤者是張志新(化名)和其女友劉穎烛缔,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體轩拨,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡践瓷,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了气嫁。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片当窗。...
    茶點故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖寸宵,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情元咙,我是刑警寧澤梯影,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站庶香,受9級特大地震影響甲棍,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜赶掖,卻給世界環(huán)境...
    茶點故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一感猛、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧奢赂,春花似錦陪白、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽立由。三九已至,卻和暖如春序厉,著一層夾襖步出監(jiān)牢的瞬間锐膜,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工弛房, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留道盏,地道東北人。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓文捶,卻偏偏與公主長得像捞奕,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子拄轻,可洞房花燭夜當晚...
    茶點故事閱讀 45,685評論 2 360

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