對象的消息轉(zhuǎn)發(fā)機制

當消息發(fā)送給一個對象時,objc_msgSend通過對象的isa指針獲取到類的結構體,然后在方法分發(fā)表里面查找方法的selector。如果沒有找到selector,則通過objc_msgSend結構體中的指向父類的指針找到其父類满败,并在父類的分發(fā)表里面查找方法的selector。依此叹括,會一直沿著類的繼承體系到達NSObject類算墨。一旦定位到selector,函數(shù)會就獲取到了實現(xiàn)的入口點领猾,并傳入相應的參數(shù)來執(zhí)行方法的具體實現(xiàn)米同。如果最后沒有定位到selector骇扇,則會走消息轉(zhuǎn)發(fā)流程

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

當一個對象能接收一個消息時,就會走正常的方法調(diào)用流程面粮。但如果一個對象無法接收指定消息時少孝,又會發(fā)生什么事呢?默認情況下熬苍,如果是以[object message]的方式調(diào)用方法稍走,如果object無法響應message消息時,編譯器會報錯柴底。但如果是以perform...的形式來調(diào)用婿脸,則需要等到運行時才能確定object是否能接收message消息。如果不能柄驻,則程序崩潰狐树。

通常,當我們不能確定一個對象是否能接收某個消息時鸿脓,會先調(diào)用respondsToSelector:來判斷一下抑钟。如下代碼所示:

if ([self respondsToSelector:@selector(method)]) {
    [self performSelector:@selector(method)];
}

當一個對象無法接收某一消息時,就會啟動所謂”消息轉(zhuǎn)發(fā)(message forwarding)“機制野哭,通過這一機制在塔,我們可以告訴對象如何處理未知的消息。默認情況下拨黔,對象接收到未知的消息蛔溃,會導致程序崩潰,通過控制臺篱蝇,我們可以看到以下異常信息:

-[SUTRuntimeMethod method]: unrecognized selector sent to instance 0x100111940
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[SUTRuntimeMethod method]: unrecognized selector sent to instance 0x100111940'

這段異常信息實際上是由NSObject的”doesNotRecognizeSelector“方法拋出的贺待。不過,我們可以采取一些措施态兴,讓我們的程序執(zhí)行特定的邏輯狠持,而避免程序的崩潰疟位。

  • 消息轉(zhuǎn)發(fā)機制基本上分為三個步驟:

1.動態(tài)方法解析
2.備用接收者
3.完整轉(zhuǎn)發(fā)
下面我們詳細討論一下這三個步驟瞻润。

動態(tài)方法解析

對象在接收到未知的消息時,首先會調(diào)用所屬類的類方法+resolveInstanceMethod:(實例方法)或者+resolveClassMethod:(類方法)甜刻。在這個方法中绍撞,我們有機會為該未知消息新增一個”處理方法””。不過使用該方法的前提是我們已經(jīng)實現(xiàn)了該”處理方法”,只需要在運行時通過class_addMethod函數(shù)動態(tài)添加到類里面就可以了,也就是變相的'將已經(jīng)實現(xiàn)的處理方法的實現(xiàn)地址綁定給”未知消息”';

+ (BOOL)resolveInstanceMethod:(SEL)sel {
    
    if (sel == @selector(notFunc)) {
        class_addMethod([self class], sel, class_getMethodImplementation([self class], @selector(func)), "v@:");
    }
    return [super resolveInstanceMethod:sel];
}
備用接收者

如果在上一步無法處理消息得院,則Runtime會繼續(xù)調(diào)以下方法:

  • (id)forwardingTargetForSelector:(SEL)aSelector
    如果一個對象實現(xiàn)了這個方法傻铣,并返回一個非nil的結果,則這個對象會作為消息的新接收者祥绞,且消息會被分發(fā)到這個對象非洲。當然這個對象不能是self自身鸭限,否則就是出現(xiàn)無限循環(huán)。當然两踏,如果我們沒有指定相應的對象來處理aSelector败京,則應該調(diào)用父類的實現(xiàn)來返回結果。

使用這個方法通常是在對象內(nèi)部梦染,可能還有一系列其它對象能處理該消息赡麦,我們便可借這些對象來處理消息并返回,這樣在對象外部看來帕识,還是由該對象親自處理了這一消息泛粹。如下代碼所示:
//消息重定向(即發(fā)送消息給其他的有這個方法的類)

 - (id)forwardingTargetForSelector:(SEL)aSelector {
 
    if (aSelector == @selector(notFunc)) {
        if (class_respondsToSelector([Person  class], aSelector)) {
            return [Person new];
        }
     }
 return [super forwardingTargetForSelector:aSelector];
 }

這一步合適于我們只想將消息轉(zhuǎn)發(fā)到另一個能處理該消息的對象上。但這一步無法對消息進行處理肮疗,如操作消息的參數(shù)和返回值晶姊。

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

如果在上一步還不能處理未知消息,則唯一能做的就是啟用完整的消息轉(zhuǎn)發(fā)機制了伪货。此時會調(diào)用以下方法:

- (void)forwardInvocation:(NSInvocation *)anInvocation

運行時系統(tǒng)會在這一步給消息接收者最后一次機會將消息轉(zhuǎn)發(fā)給其它對象帽借。對象會創(chuàng)建一個表示消息的NSInvocation對象,把與尚未處理的消息有關的全部細節(jié)都封裝在anInvocation中超歌,包括selector砍艾,目標(target)和參數(shù)。我們可以在forwardInvocation方法中選擇將消息轉(zhuǎn)發(fā)給其它對象巍举。
forwardInvocation:方法的實現(xiàn)有兩個任務:

  • 定位可以響應封裝在anInvocation中的消息的對象脆荷。這個對象不需要能處理所有未知消息。
  • 使用anInvocation作為參數(shù)懊悯,將消息發(fā)送到選中的對象蜓谋。anInvocation將會保留調(diào)用結果,運行時系統(tǒng)會提取這一結果并將其發(fā)送到消息的原始發(fā)送者炭分。
    不過桃焕,在這個方法中我們可以實現(xiàn)一些更復雜的功能,我們可以對消息的內(nèi)容進行修改捧毛,比如追回一個參數(shù)等观堂,然后再去觸發(fā)消息。另外呀忧,若發(fā)現(xiàn)某個消息不應由本類處理师痕,則應調(diào)用父類的同名方法,以便繼承體系中的每個類都有機會處理此調(diào)用請求而账。
    還有一個很重要的問題胰坟,我們必須重寫以下方法:
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector

消息轉(zhuǎn)發(fā)機制使用從這個方法中獲取的信息來創(chuàng)建NSInvocation對象。因此我們必須重寫這個方法泞辐,為給定的selector提供一個合適的方法簽名笔横。

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
 NSMethodSignature *s = [super methodSignatureForSelector:aSelector];
    if (!s) {
        s = [[Person new] methodSignatureForSelector:aSelector];
    }
    return s;
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
    if (class_respondsToSelector([Person class], anInvocation.selector)) {
        [anInvocation invokeWithTarget:[Person new]];
    }else {
        [super forwardInvocation: anInvocation];
    }
}

NSObject的forwardInvocation:方法實現(xiàn)只是簡單調(diào)用了doesNotRecognizeSelector:方法竞滓,它不會轉(zhuǎn)發(fā)任何消息。這樣吹缔,如果不在以上所述的三個步驟中處理未知消息虽界,則會引發(fā)一個異常。

從某種意義上來講涛菠,forwardInvocation:就像一個未知消息的分發(fā)中心莉御,將這些未知的消息轉(zhuǎn)發(fā)給其它對象∷锥常或者也可以像一個運輸站一樣將所有未知消息都發(fā)送給同一個接收對象礁叔。這取決于具體的實現(xiàn)。

參考

Objective-C Runtime 運行時之三:方法與消息

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末迄薄,一起剝皮案震驚了整個濱河市琅关,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌讥蔽,老刑警劉巖涣易,帶你破解...
    沈念sama閱讀 221,576評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異冶伞,居然都是意外死亡新症,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評論 3 399
  • 文/潘曉璐 我一進店門响禽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來徒爹,“玉大人,你說我怎么就攤上這事芋类÷⌒幔” “怎么了?”我有些...
    開封第一講書人閱讀 168,017評論 0 360
  • 文/不壞的土叔 我叫張陵侯繁,是天一觀的道長胖喳。 經(jīng)常有香客問我,道長贮竟,這世上最難降的妖魔是什么丽焊? 我笑而不...
    開封第一講書人閱讀 59,626評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮坝锰,結果婚禮上粹懒,老公的妹妹穿的比我還像新娘重付。我一直安慰自己顷级,他們只是感情好,可當我...
    茶點故事閱讀 68,625評論 6 397
  • 文/花漫 我一把揭開白布确垫。 她就那樣靜靜地躺著弓颈,像睡著了一般帽芽。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上翔冀,一...
    開封第一講書人閱讀 52,255評論 1 308
  • 那天导街,我揣著相機與錄音,去河邊找鬼纤子。 笑死搬瑰,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的控硼。 我是一名探鬼主播泽论,決...
    沈念sama閱讀 40,825評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼卡乾!你這毒婦竟也來了翼悴?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,729評論 0 276
  • 序言:老撾萬榮一對情侶失蹤幔妨,失蹤者是張志新(化名)和其女友劉穎鹦赎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體误堡,經(jīng)...
    沈念sama閱讀 46,271評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡古话,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,363評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了锁施。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片煞额。...
    茶點故事閱讀 40,498評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖沾谜,靈堂內(nèi)的尸體忽然破棺而出膊毁,到底是詐尸還是另有隱情,我是刑警寧澤基跑,帶...
    沈念sama閱讀 36,183評論 5 350
  • 正文 年R本政府宣布婚温,位于F島的核電站,受9級特大地震影響媳否,放射性物質(zhì)發(fā)生泄漏栅螟。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,867評論 3 333
  • 文/蒙蒙 一篱竭、第九天 我趴在偏房一處隱蔽的房頂上張望力图。 院中可真熱鬧,春花似錦掺逼、人聲如沸吃媒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽赘那。三九已至刑桑,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間募舟,已是汗流浹背祠斧。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留拱礁,地道東北人琢锋。 一個月前我還...
    沈念sama閱讀 48,906評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像呢灶,于是被迫代替她去往敵國和親吩蔑。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,507評論 2 359

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

  • 對象在收到無法解讀的消息會發(fā)生什么? 若想令類理解某條消息,必須有對應的實現(xiàn)方法.但是,在編譯期間動態(tài)像對象添加方...
    有一種再見叫青春閱讀 582評論 0 2
  • 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的...
    西木閱讀 30,568評論 33 466
  • 我們常常會聽說 Objective-C 是一門動態(tài)語言填抬,那么這個「動態(tài)」表現(xiàn)在哪呢烛芬?我想最主要的表現(xiàn)就是 Obje...
    Ethan_Struggle閱讀 2,199評論 0 7
  • 轉(zhuǎn)至元數(shù)據(jù)結尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,725評論 0 9
  • 現(xiàn)在是最壞的時代飒责,也是最好的時代赘娄。 在這個時代比過去任何時候競爭都要激烈,生活壓力巨大宏蛉,不僅是物質(zhì)上的遣臼,還有精神上...
    終身學習的昊天閱讀 303評論 0 0