1. 程式人生 > >訊息轉發-什麼時候會報unrecognized selector的異常?

訊息轉發-什麼時候會報unrecognized selector的異常?

簡單來說:

當呼叫該物件上某個方法,而該物件上沒有實現這個方法的時候, 可以通過“訊息轉發”進行解決。
簡單的流程如下,在上一題中也提到過:

objc是動態語言,每個方法在執行時會被動態轉為訊息傳送,即:objc_msgSend(receiver, selector)。
objc在向一個物件傳送訊息時,runtime庫會根據物件的isa指標找到該物件實際所屬的類,然後在該類中的方法列表以及其父類方法列表中尋找方法執行,如果,在最頂層的父類中依然找不到相應的方法時,程式在執行時會掛掉並丟擲異常unrecognized selector sent to XXX 。但是在這之前,objc的執行時會給出三次拯救程式崩潰的機會:

Method resolution

objc執行時會呼叫+resolveInstanceMethod:或者 +resolveClassMethod:,讓你有機會提供一個函式實現。如果你添加了函式,那執行時系統就會重新啟動一次訊息傳送的過程,否則 ,執行時就會移到下一步,訊息轉發(Message Forwarding)。

Fast forwarding

如果目標物件實現了-forwardingTargetForSelector:,Runtime 這時就會呼叫這個方法,給你把這個訊息轉發給其他物件的機會。 只要這個方法返回的不是nil和self,整個訊息傳送的過程就會被重啟,當然傳送的物件會變成你返回的那個物件。否則,就會繼續Normal Fowarding。 這裡叫Fast,只是為了區別下一步的轉發機制。因為這一步不會建立任何新的物件,但下一步轉發會建立一個NSInvocation物件,所以相對更快點。

Normal forwarding

這一步是Runtime最後一次給你挽救的機會。首先它會發送-methodSignatureForSelector:訊息獲得函式的引數和返回值型別。如果-methodSignatureForSelector:返回nil,Runtime則會發出-doesNotRecognizeSelector:訊息,程式這時也就掛掉了。如果返回了一個函式簽名,Runtime就會建立一個NSInvocation物件併發送-forwardInvocation:訊息給目標物件。

為了能更清晰地理解這些方法的作用,git倉庫裡也給出了一個Demo,名稱叫“ _objc_msgForward_demo ”,可執行起來看看。