前言
一個方法的聲明必定會有與之對應的實現,如果調用了只有聲明沒有實現的方法會導致程序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:
以實例方法為例:
說一下這個函數
class_addMethod(<#__unsafe_unretained Class cls#>, <#SEL name#>, <#IMP imp#>, <#const char *types#>)
cls
表示要添加方法的類,name
表示要添加方法的SEL
,imp
表示要添加方法的IMP
,types
表要添加方法的返回值和參數類型。
上篇文章已經說過SEL
和IMP
的異同,也許你會想這里的name
和imp
參數是否重復,畢竟receiver
已經明確,無論通過SEL
還是IMP
都是可以找到對應函數\方法的。正常情況是這樣,但是這里只有聲明沒有常規的實現,如果只有SEL
會導致找不到入口,如果只有IMP
會導致函數\方法名不確定。
示例中type
的值為v@:@
,如果不用imp_implementationWithBlock
來寫可能更好理解
v@:@
中:v
對應著返回值void
,第一個@
對應著第一個參數類型id
,:
對應著第二個參數類型SEL
,第二個@
對應著三個參數類型NSString
關于Type Encodings可參考官方文檔
forwardingTargetForSelector:
將ViewController中調用的實例方法轉移到Message中,這里很好理解無須贅述。
forwardInvocation:
同樣,這里是將ViewController中調用的實例方法轉移到Message中,只是多了一層NSInvocation
包裝,有利于我們做更多的事情。