Runtime系列(消息轉發)

前言

一個方法的聲明必定會有與之對應的實現,如果調用了只有聲明沒有實現的方法會導致程序crash,而實現并非只有中規中矩的在.m里寫上相同的方法名再在內部寫實現代碼。

正文

先來回顧一下引文中的部分內容:
當調用[receiver message]時,會觸發id objc_msgSend(id self, SEL op, ...)這個函數。
receiver通過isa指針找到當前對象的class,并在class中尋找op,如果找到,調用op,如果沒找到,到super_class中繼續尋找,如此循環直到NSObject(引自引文)。
如果NSObject中仍然沒找到,程序并不會立即crash,而是按照優先級執行下列三個方法(下列方法優先級依次遞減,高優先級方法消息轉發成功不會再執行低優先級方法):

1.+ resolveInstanceMethod:(SEL)sel // 對應實例方法
  + resolveClassMethod:(SEL)sel // 對應類方法
2.- (id)forwardingTargetForSelector:(SEL)aSelector
3.- (void)forwardInvocation:(NSInvocation *)anInvocation
舉例

比如在ViewController.h中聲明,并且不在ViewController.m中直接實現,如何確保程序正常運行

@interface ViewController : UIViewController
- (void)sayHello:(NSString *)name;

@end
resolveInstanceMethod:

以實例方法為例:

resolveInstanceMethod 1.png

說一下這個函數

class_addMethod(<#__unsafe_unretained Class cls#>, <#SEL name#>, <#IMP imp#>, <#const char *types#>)

cls表示要添加方法的類,name表示要添加方法的SELimp表示要添加方法的IMPtypes表要添加方法的返回值和參數類型。
上篇文章已經說過SELIMP的異同,也許你會想這里的nameimp參數是否重復,畢竟receiver已經明確,無論通過SEL還是IMP都是可以找到對應函數\方法的。正常情況是這樣,但是這里只有聲明沒有常規的實現,如果只有SEL會導致找不到入口,如果只有IMP會導致函數\方法名不確定。
示例中type的值為v@:@,如果不用imp_implementationWithBlock來寫可能更好理解

resolveInstanceMethod 2.png

v@:@中:v對應著返回值void第一個@對應著第一個參數類型id:對應著第二個參數類型SEL第二個@對應著三個參數類型NSString關于Type Encodings可參考官方文檔

forwardingTargetForSelector:
forwardingTargetForSelector.png
Message.h.png
Messsage.m.png

將ViewController中調用的實例方法轉移到Message中,這里很好理解無須贅述。

forwardInvocation:
forwardInvocation.png

同樣,這里是將ViewController中調用的實例方法轉移到Message中,只是多了一層NSInvocation包裝,有利于我們做更多的事情。

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

推薦閱讀更多精彩內容