iOS的消息轉發機制

一、簡介:

消息轉發是OC底層一種功能強大的實現,為OC方法的調用增加更多的表現力和容錯能力。什么是消息轉發?簡單來說,就是在OC在調用方法但不能找到方法對應實現時,執行的一種補救措施,從而將方法的執行引導到其他地方,為程序執行提供更多可能.

二、先來了解什么是消息發送

消息發送的官方定義:

屏幕快照 2019-01-25 上午11.24.37.png

官方文檔

OC的方法本質:
OC方法的底層實現就是objc_msgSend()
objc_msgSend()前面兩個參數:selfSEL。對self的理解一般認為它是對象本身,官方文檔的解釋是指向接收此消息的對象的指針,其實也不難理解,按照runtime的邏輯,方法的執行先要查找到本類的方法列表,然后執行,因此就需要知道本類是誰。對于_cmd(它保存了正在發送的消息的選擇器)是第二個隱式參數,對應方法的實現。總之,self指向對象本身,_cmd指向方法本身。

OC中方法分為類方法和對象方法,對應的調用方式就是:
1.類名調用類方法:

// 對象實例調用
Person *person = [[Person alloc] init];
[person run];

2.對象實例調用實例方法:

// 類方法調用
[Person walk];

OC 函數調用的語法都會被翻譯成一個 C 的函數調用 objc_msgSend()
我們用對象調用方法來舉例子說明

1.先自定義一個Person類:


屏幕快照 2019-01-24 下午4.54.05.png

2.分別用person對象和消息發送來調用run方法:


屏幕快照 2019-01-24 下午4.53.10.png

屏幕快照 2019-01-24 下午5.09.46.png

3.查看打印結果:


屏幕快照 2019-01-24 下午4.53.27.png
屏幕快照 2019-01-24 下午5.10.08.png

4.可以看到打印了三次,說明方法被調用了三次.其中對象調用一次, objc_msgSend()調用了兩次. objc_msgSend()就是方法調用的底層實現.

說明:
可以看到上面寫了兩種方式的objc_msgSend()調用,這是是LLVM的配置選項,可以選擇關閉objc_msgSend的編寫檢查.具體操作如圖:

屏幕快照 2019-01-24 下午5.08.20.png

5.消息發送的具體細節實現會另外寫一篇文章.

二、消息轉發...

當沒有方法的實現,程序會在運行時掛掉并拋出 unrecognized selector sent to …的異常。但在異常拋出前,Objective-C 的運行時會給你三次拯救程序的機會:

  • 動態方法解析: Method Resolution
  • 快速轉發: Fast Rorwarding
  • 完整消息轉發: Normal Forwarding

系統在處理消息轉發的時候,是按照上面的順序進行轉發的,轉發成功則會跳過后面的方法.

1.動態方法解析: Method Resolution

首先,當調用沒有實現的方法的時候,Objective-C 運行時會調用 + (BOOL)resolveInstanceMethod:或者 + (BOOL)resolveClassMethod:,讓你有機會提供一個函數實現。如果你添加了函數并返回 YES, 那運行時系統就會重新啟動一次消息發送的過程。

屏幕快照 2019-01-24 下午5.48.47.png

這里
v 代表函數返回類型void,
@ 代表self的類型id,
: 代表_cmd的類型SEL。

2.快速轉發: Fast Rorwarding

  • Method Resolution 不同,Fast Rorwarding 這是一種快速消息轉發:只需要在- (id)forwardingTargetForSelector:(SEL)aSelector 方法里面返回一個新對象即可。相當于替換了消息的接收者,進而去新的接收者那里去尋找對應的實現。
  • 通過- (id)forwardingTargetForSelector:(SEL)aSelector方法。如果此方法返回的是新的消息接收對象,則會向新對象轉發此消息,如果此方法返回的是 nil 或者self,則會進入系統消息轉發機制。具體為向 - (void)forwardInvocation:(NSInvocation *)invocation方法轉發.
屏幕快照 2019-01-24 下午5.58.52.png

3. 完整消息轉發: Normal Forwarding

與上面不同,可以理解成完整消息轉發,用來代替快速轉發做更多的事。

屏幕快照 2019-01-25 上午9.46.06.png

methodSignatureForSelector用來生成方法簽名,這個簽名就是給 forwardInvocation中的參數 NSInvocation調用的。通過NSInvocation中的SEL來執行下面的轉發邏輯.

  • NSInvocation 的內部結構:


    屏幕快照 2019-01-25 上午10.18.15.png

1.methodSignatureForSelector這個方法中,如果沒有找到方法對應的實現,就會返回一個空的方法簽名,最終NSObject找不到SEL。系統就會報開頭我們提到的 unrecognized selector sent to instance錯誤,最終導致程序報錯崩潰。
2.所以我們需要做的是自己新建方法簽名,再在forwardInvocation中用你要轉發的那個對象調用這個對應的簽名,這樣也實現了消息轉發。

屏幕快照 2019-01-25 上午10.24.23.png

上圖中methodSignature為nil,導致-[NSObject(NSObject) doesNotRecognizeSelector:] 報錯,引起程序崩潰.

三、總結

OC方法的調用通過消息發送的形式實現,當方法的實現找不到的情況下,運行時環境會依次進行下面三個階段的查找:

第一階段:
  • (BOOL)resolveInstanceMethod:(SEL)name(實例方法)
  • (BOOL)resolveClassMethod:(SEL)name(類方法)
第二階段:
  • (id)forwardingTargetForSelector:(SEL)aSelector(快速轉發)

在此方法中另外返回一個類的對象,該類含有對應方法的實現,runtime會在新類的方法列表中進行查找,找到就去執行,找不到依然會報錯.

第三階段:
  • (void)forwardInvocation:(NSInvocation *)invocation
  • (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector

methodSignatureForSelector中實現方法簽名, forwardInvocation中根據methodSignatureForSelector返回的方法簽名進行消息的轉發.

參考鏈接:
http://www.lxweimin.com/p/2fd4b930588e
http://www.lxweimin.com/p/1bde36ad9938

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,197評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,415評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,104評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,884評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,647評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,130評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,208評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,366評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,887評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,737評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,939評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,478評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,174評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,586評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,827評論 1 283
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,608評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,914評論 2 372

推薦閱讀更多精彩內容

  • 轉至元數據結尾創建: 董瀟偉,最新修改于: 十二月 23, 2016 轉至元數據起始第一章:isa和Class一....
    40c0490e5268閱讀 1,747評論 0 9
  • 消息發送和轉發流程可以概括為:消息發送(Messaging)是 Runtime 通過 selector 快速查找 ...
    lylaut閱讀 1,871評論 2 3
  • 轉載:http://yulingtianxia.com/blog/2014/11/05/objective-c-r...
    F麥子閱讀 753評論 0 2
  • 關于OC中的消息發送的實現,在去年也看過一次,當時有點不太理解,但是今年再看卻很容易理解。 我想這跟知識體系的構建...
    咖啡綠茶1991閱讀 975評論 0 1
  • 說是急雨,不如說人心焦急 兩點打雷,六點才下 雨,劈劈啪啪 鐵棚被砸痛 匆忙的腳步躲不及 一道閃電照亮一角陰暗 剎...
    落花兒閱讀 195評論 0 0