天天看點

Objective-C運作時消息派發機制Objective-C運作時消息派發機制

Objective-C運作時消息派發機制

“當看到一隻鳥走起來像鴨子、遊泳起來像鴨子、叫起來也像鴨子,那麼這隻鳥就可以被稱為鴨子。”

Objective-C是一門動态語言,鴨子類型是動态語言的重要特性,而id則是Objective-C中最大的一隻鴨子。

而Objective-C是如何實作鴨子類型的?這就必須從OC運作是消息派發機制說起。

我們知道當我們的OC對象收到一條看不懂的資訊時,會調用doesNotRecognizeSelector并抛出異常,表現在外就是Crash。

然而在抛出異常之前我們有很多機會來教會OC對象來如何閱讀這條消息,或者讓他看不懂丢給看的懂的對象。

OC對象的方法綁定是被OC Runtime系統推遲到了Runtime,編譯器在這裡隻能給有限的檢查和排錯。(因為在這裡編譯器并不能像對靜态語言那樣做嚴格的檢查。關于動态語言和靜态語言的争議,蘋果給了他自己的答案,swift是編譯時的靜态語言,然而運作時卻是動态的,這意味着什麼自己去思考)。

是以方法的調用在運作時經曆了一場非比尋常的旅行。
在Class初始化的時候,method list,method cache也被初始化,method list顧名思義,method cache略過不表。
           

首先向對象發送一條消息

1、對象翻了下Method list小本子,發現自己不知道這條消息在表達什麼意思,于是通過isa向上求助super class,如果可以super知道是怎麼回事,真是皆大歡喜,如果super也不會→_→,往下走。(此處有Method cache相關過程,略過不表,感興趣可以翻一翻相關文檔)

2、執行resolvInstanceMethod,詢問一下是不是現在立馬學一下這個SEL怎麼響應的。在這裡我們可以将備用的IMP插入。傳回YES,則在執行一次這個msg目前所對應的IMP。恩恩,我現在可以不想學嗎?傳回NO,→_→,往下走。

3、開啟甩鍋模式,執行forwardingTargetForSelector,既然我不會,傳回一個可能知道怎麼響應這條msg的對象。恩,由傳回的這個對象繼續執行以上1-N的步驟;人緣好差,居然這個時候沒有對象來願意來接鍋,傳回nil。→_→,往下走。

4、執行methodSignatureForSelector來擷取方法簽名。在這裡可以找個DuckType來擷取指定SEL的方法簽名。或者在這裡動态的創造一個方法簽名并傳回;如果無法擷取,傳回nil,隻能遺憾的執行doesNotRecognizeSelector并抛出異常。

5、執行forwardInvocation,這裡是最後一次對msg響應的時機,在這裡要麼對invocation找到正确的響應方式,要麼調用super,抛異常(當然也可以不調用)。