iOS消息轉(zhuǎn)發(fā)機(jī)制

“好記性不如爛筆頭”,雖然我不是用的筆。但是敲一遍對我來說效果肯定比單純的看一遍效果更好!!!


若想令類能夠理解某條消息,我們必須實(shí)現(xiàn)出對應(yīng)的方法才行。但是,在編譯器向類發(fā)送其無法解讀的消息時并不會報錯,因?yàn)樵谶\(yùn)行期可以繼續(xù)向類中添加方法,所以編譯器在編譯時還無法確定類中到底會不會有某個方法的實(shí)現(xiàn)。當(dāng)對象接收到無法解讀的消息時,就會啟動“消息轉(zhuǎn)發(fā)”機(jī)制,我們可以經(jīng)由此過程告訴對象應(yīng)該如何處理未知消息。

消息轉(zhuǎn)發(fā)分為兩個階段。第一階段先征詢接收者所屬的類,看其是否能動態(tài)添加方法,已處理當(dāng)前這個“未知的選擇子”,這叫做“動態(tài)方法解析”。第二階段涉及“完整的消息轉(zhuǎn)發(fā)機(jī)制”。如果運(yùn)行期系統(tǒng)已經(jīng)把第一階段執(zhí)行完了,那么接收者自己就無法再以動態(tài)新增方法的手段來響應(yīng)包含該選擇子的消息了。此時運(yùn)行期系統(tǒng)就會請求接收者以其他手段來處理與消息相關(guān)的方法調(diào)用。細(xì)分為兩步:首先,讓接收者看看有沒有其他對象能處理這條消息。如果有,則運(yùn)行期系統(tǒng)會把消息轉(zhuǎn)給那個接收者,于是消息轉(zhuǎn)發(fā)結(jié)束。如果沒有這個“備援接收者”,則啟動完整的消息轉(zhuǎn)發(fā)機(jī)制,運(yùn)行期系統(tǒng)會把與消息有關(guān)的全部細(xì)節(jié)封裝到NSInvocation對象中,再給接收者最后一次機(jī)會,令其設(shè)法解決當(dāng)前還未處理的這條消息。

動態(tài)方法解析:

對象在收到無法解讀的消息之后,首先將調(diào)用所屬類的下列類方法:

圖1

該方法的參數(shù)就是那個未知的選擇子,其返回值為布爾類型,表示這個類是否能新增一個實(shí)例方法用以處理此選擇子。在繼續(xù)往下執(zhí)行轉(zhuǎn)發(fā)機(jī)制之前,本類有一個機(jī)會處理此選擇子方法。假如是一個類方法,那么將會調(diào)用另外一個方法,如圖2:

圖2

備援接收者:

當(dāng)前接收者還有第二次機(jī)會處理未知的選擇子,在這一步中,運(yùn)行期系統(tǒng)會詢問是否能將該消息轉(zhuǎn)發(fā)給其他的接收者處理。對應(yīng)的方法如圖3:

圖3

參數(shù)為未知的選擇子,如當(dāng)前接收者能找到備援對象,則將其返回,找不到就返回nil。通過此方案,我們可以用“組合”來模擬出“多重繼承”的某些特性。在一個對象內(nèi)部,可能還有一系列其他對象,該對象可以經(jīng)由此方法將能夠處理某選擇子的相關(guān)內(nèi)部對象返回,這樣的話,在外界看來好像是該對象親自處理了這些消息。

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

如果轉(zhuǎn)發(fā)已經(jīng)到了這一步,那么唯一能做的就是啟動完整的消息轉(zhuǎn)發(fā)機(jī)制了。首先創(chuàng)建NSIvocation對象,把尚未處理的那條消息有關(guān)的細(xì)節(jié)全部封到其中。此對象包含選擇子、目標(biāo)、參數(shù)。在出發(fā)NSIvocation對象時,”消息派發(fā)系統(tǒng)“將親自出馬,把消息指派給目標(biāo)對象。會調(diào)用圖4的方法來轉(zhuǎn)發(fā)消息:

圖4

這個方法的實(shí)現(xiàn)可以寫的很簡單,只需要改變調(diào)用目標(biāo),使消息在新目標(biāo)上得以調(diào)用即可。然而這樣實(shí)現(xiàn)出來的方法與”備援接收者“反感所實(shí)現(xiàn)的方法等效,所以很少有人采用這么簡單的實(shí)現(xiàn)方式。比較有用的實(shí)現(xiàn)方式為:在觸發(fā)消息前,先以某種方式改變消息內(nèi)容,比如追加另外一個參數(shù),或者是改換選擇子等等。實(shí)現(xiàn)此方法時若發(fā)現(xiàn)不應(yīng)該由本類處理,則需要調(diào)用超類的同名方法。這樣的話,集成體系中的每個類都有機(jī)會處理此調(diào)用請求,直至NSObject。如果最后調(diào)用了NSObject類的方法,那么該方法還會繼而調(diào)用”doesNotRecognizeSelector:“以拋出異常,此異常表明選擇子最終未能得到處理。

消息轉(zhuǎn)發(fā)流程:

下面這張圖描述了消息轉(zhuǎn)發(fā)機(jī)制處理消息的各個步驟:

圖5

接收者在每一步中均有機(jī)會處理消息。步驟越往后,處理消息的代價就會越大。最好能在第一步就完成,這樣的話,運(yùn)行期系統(tǒng)就可以將此方法緩存起來。如果這個類的實(shí)例后面還收到同名的選擇子,那么根本就無須啟動消息轉(zhuǎn)發(fā)流程。若想在第三部把消息轉(zhuǎn)發(fā)給備援接收者,還不如把轉(zhuǎn)發(fā)操作提前到第二部。以為第三部只是修改了調(diào)用目標(biāo),這項(xiàng)改動放在第二部執(zhí)行的話會更加簡單,不然還得創(chuàng)建并處理完整的NSIvocation。

以完整的例子演示動態(tài)方法解析:

為了說明消息轉(zhuǎn)發(fā)機(jī)制的意義,下面示范如何以動態(tài)方法解析來實(shí)現(xiàn)@dynamic屬性。假設(shè)要編寫一個類似于”字典的對象“,它里面可以容納其他對象,只不過開發(fā)者要直接通過屬性來存取其中的數(shù)據(jù)。這個類的設(shè)計思路是:由開發(fā)者來添加數(shù)據(jù)定義,并將其聲明為@dynamic,而類則會自動處理相關(guān)屬性值得存放與獲取操作。

本例的關(guān)在在resolveInstanceMethod:方法的實(shí)現(xiàn):

圖6

具體demo在這里,里面有詳細(xì)的注釋!!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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