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

動態(tài)方法解析和轉(zhuǎn)發(fā)
Message Forward.jpg

在上面的例子中穴吹,如果 foo沒有找到會發(fā)生什么握联?通常情況下笑陈,程序會在運行時掛掉并拋出 unrecognized selector sent to … 的異常。但在異常拋出前工秩,Objective-C 的運行時會給你三次拯救程序的機會:

  1. Method resolution
  2. Fast forwarding
  3. Normal forwarding
Method Resolution

首先,Objective-C 運行時會調(diào)用 +resolveInstanceMethod:
或者 +resolveClassMethod:进统,讓你有機會提供一個函數(shù)實現(xiàn)助币。如果你添加了函數(shù)并返回 YES, 那運行時系統(tǒng)就會重新啟動一次消息發(fā)送的過程螟碎。還是以 foo
為例眉菱,你可以這么實現(xiàn):

void fooMethod(id obj, SEL _cmd)
 { 
      NSLog(@"Doing foo");
}
+ (BOOL)resolveInstanceMethod:(SEL)aSEL
{
     if(aSEL == @selector(foo:))
     { 
            class_addMethod([self class], aSEL, (IMP)fooMethod, "v@:"); return YES; 
     } 
    return [super resolveInstanceMethod];
}

如果 resolve 方法返回 NO ,運行時就會移到下一步:消息轉(zhuǎn)發(fā)(Message Forwarding)掉分。

PS:iOS 4.3 加入很多新的 runtime 方法俭缓,主要都是以 imp 為前綴的方法克伊,比如 imp_implementationWithBlock()用 block 快速創(chuàng)建一個 imp 。 上面的例子可以重寫成:

IMP fooIMP = imp_implementationWithBlock(^(id _self) { NSLog(@"Doing foo");
});
class_addMethod([self class], aSEL, fooIMP, "v@:"); 

Fast forwarding

如果目標(biāo)對象實現(xiàn)了 -forwardingTargetForSelector: 华坦,Runtime 這時就會調(diào)用這個方法愿吹,給你把這個消息轉(zhuǎn)發(fā)給其他對象的機會。

- (id)forwardingTargetForSelector:(SEL)aSelector
{ 
      if(aSelector == @selector(foo:))
      { 
          return alternateObject; 
      } 
      return [super forwardingTargetForSelector:aSelector];
}

只要這個方法返回的不是 nil 和 self惜姐,整個消息發(fā)送的過程就會被重啟犁跪,當(dāng)然發(fā)送的對象會變成你返回的那個對象。否則歹袁,就會繼續(xù) Normal Fowarding 坷衍。

Normal forwarding

這一步是 Runtime 最后一次給你挽救的機會。首先它會發(fā)送 -
methodSignatureForSelector:消息獲得函數(shù)的參數(shù)和返回值類型条舔。如果 -methodSignatureForSelector:返回 nil 枫耳,Runtime 則會發(fā)出 -doesNotRecognizeSelector:消息,程序這時也就掛掉了逞刷。
如果返回了一個函數(shù)簽名嘉涌,Runtime 就會創(chuàng)建一個 NSInvocation 對象并發(fā)送 -forwardInvocation:消息給目標(biāo)對象妻熊。

NSInvocation 實際上就是對一個消息的描述夸浅,包括selector 以及參數(shù)等信息。所以你可以在 -forwardInvocation:
里修改傳進(jìn)來的 NSInvocation 對象扔役,然后發(fā)送 -invokeWithTarget:
消息給它帆喇,傳進(jìn)去一個新的目標(biāo):

- (void)forwardInvocation:(NSInvocation *)invocation
{ 
        SEL sel = invocation.selector; 
        if([alternateObject respondsToSelector:sel]) 
        { 
            [invocation invokeWithTarget:alternateObject]; 
        } 
        else 
        {
           [self doesNotRecognizeSelector:sel]; 
        }
}

Cocoa 里很多地方都利用到了消息傳遞機制來對語言進(jìn)行擴展,如 Proxies亿胸、NSUndoManager 跟 Responder Chain坯钦。NSProxy 就是專門用來作為代理轉(zhuǎn)發(fā)消息的;NSUndoManager 截取一個消息之后再發(fā)送侈玄;而 Responder Chain 保證一個消息轉(zhuǎn)發(fā)給合適的響應(yīng)者婉刀。

總結(jié)

Objective-C 中給一個對象發(fā)送消息會經(jīng)過以下幾個步驟:

  1. 在對象類的 dispatch table 中嘗試找到該消息。如果找到了序仙,跳到相應(yīng)的函數(shù)IMP去執(zhí)行實現(xiàn)代碼突颊;

  2. 如果沒有找到,Runtime 會發(fā)送 +resolveInstanceMethod:或者 +resolveClassMethod:嘗試去 resolve 這個消息潘悼;

  3. 如果 resolve 方法返回 NO律秃,Runtime 就發(fā)送 -forwardingTargetForSelector:允許你把這個消息轉(zhuǎn)發(fā)給另一個對象;

  4. 如果沒有新的目標(biāo)對象返回治唤, Runtime 就會發(fā)送 -methodSignatureForSelector:和 -forwardInvocation:消息棒动。你可以發(fā)送 -invokeWithTarget:消息來手動轉(zhuǎn)發(fā)消息或者發(fā)送 -doesNotRecognizeSelector:拋出異常。

轉(zhuǎn)載:http://tech.glowing.com/cn/objective-c-runtime/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末宾添,一起剝皮案震驚了整個濱河市船惨,隨后出現(xiàn)的幾起案子柜裸,更是在濱河造成了極大的恐慌,老刑警劉巖粱锐,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件粘室,死亡現(xiàn)場離奇詭異,居然都是意外死亡卜范,警方通過查閱死者的電腦和手機衔统,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來海雪,“玉大人锦爵,你說我怎么就攤上這事“侣悖” “怎么了险掀?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長湾宙。 經(jīng)常有香客問我樟氢,道長,這世上最難降的妖魔是什么侠鳄? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任埠啃,我火速辦了婚禮,結(jié)果婚禮上伟恶,老公的妹妹穿的比我還像新娘碴开。我一直安慰自己,他們只是感情好博秫,可當(dāng)我...
    茶點故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布潦牛。 她就那樣靜靜地躺著,像睡著了一般挡育。 火紅的嫁衣襯著肌膚如雪巴碗。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天即寒,我揣著相機與錄音橡淆,去河邊找鬼。 笑死蒿叠,一個胖子當(dāng)著我的面吹牛明垢,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播市咽,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼痊银,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了施绎?” 一聲冷哼從身側(cè)響起溯革,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤贞绳,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后致稀,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體冈闭,經(jīng)...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年抖单,在試婚紗的時候發(fā)現(xiàn)自己被綠了萎攒。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,622評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡矛绘,死狀恐怖耍休,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情货矮,我是刑警寧澤羊精,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站囚玫,受9級特大地震影響喧锦,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜抓督,卻給世界環(huán)境...
    茶點故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一燃少、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧本昏,春花似錦供汛、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽雀久。三九已至宿稀,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間赖捌,已是汗流浹背祝沸。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留越庇,地道東北人罩锐。 一個月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像卤唉,于是被迫代替她去往敵國和親涩惑。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,490評論 2 348

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

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉桑驱,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,686評論 0 9
  • Objective-C 擴展了 C 語言竭恬,并加入了面向?qū)ο筇匦院?Smalltalk 式的消息傳遞機制跛蛋。而這個擴展...
    Zsz丶少閱讀 299評論 0 0
  • Objective-C 是一個動態(tài)語言,可以通過運行時系統(tǒng)來動態(tài)得創(chuàng)建類和對象痊硕、進(jìn)行消息傳遞和轉(zhuǎn)發(fā)赊级。 在Objec...
    大衛(wèi)石閱讀 243評論 0 0
  • Objective-C 擴展了 C 語言,并加入了面向?qū)ο筇匦院?Smalltalk 式的消息傳遞機制岔绸。而這個擴展...
    RobinYu閱讀 1,522評論 2 4
  • 消息轉(zhuǎn)發(fā)三部曲: 接上面消息發(fā)送理逊,如果當(dāng)前類和父類中都沒有找到實現(xiàn),那么就會開始嘗試動態(tài)方法解析盒揉。 動態(tài)方法解析 ...
    s_在路上閱讀 2,172評論 2 14