OC由于運行時特性,可以在運行期間動態(tài)添加方法,這個尋找動態(tài)添加的方法的過程就是動態(tài)消息轉(zhuǎn)發(fā)。
iOS的消息轉(zhuǎn)發(fā)機制分為三個步驟:
- 動態(tài)方法解析
- 快速消息轉(zhuǎn)發(fā)機制
- 完整消息轉(zhuǎn)發(fā)機制
1. 動態(tài)方法解析
首先是征詢接收者所屬的類,看其是否能動態(tài)添加調(diào)用的方法,來處理當(dāng)前這個未知的選擇子;
對象在無法解讀消息會首先調(diào)用所屬類的下列類方法:
+ (BOOL) resolveInstanceMethod:(SEL)selector
參數(shù)為那個未知的選擇子,返回值表示這個類能否新增一個實例方法處理此選擇子。如果尚未實現(xiàn)的方法不是實例方法而是類方法則運行期會調(diào)用另一個方法:+ (BOOL) resolveClassMethod:(SEL)selector。使用這種方法的前提是:相關(guān)方法的實現(xiàn)代碼已經(jīng)寫好,只等著運行的時候動態(tài)插入到類里面就可以了。此方案常用來實現(xiàn)@dynamic屬性。
2. 快速消息轉(zhuǎn)發(fā)
當(dāng)前接收者還有第二次機會能處理未知的選擇子,這一步中,運行期會問它:能不能把這條消息轉(zhuǎn)發(fā)給其他接收者來處理。與該步驟對應(yīng)的處理方法:
- (id)forwardingTargetForSelector:(SEL)selector
方法參數(shù)代表未知的選擇子,若當(dāng)前接收者能找到備援對象,則將其返回,若找不到就返回nil。通過此方案我們可以用“組合”來模擬出“多重繼承”的某些特性(因為OC屬于單繼承,一個字類只能繼承一個基類)。在一個對象內(nèi)部,可能還有一系列其他對象,該對象可能由此方法將能夠處理某選擇子的相關(guān)內(nèi)部對象返回,這樣的話,在外界看來,好像該對象親自處理了這些消息。
- (id)forwardingTargetForSelector:(SEL)aSelector
{
TargetObj *obj = [[TargetObj alloc]init];
if ([obj respondsToSelector:aSelector]) {
return doctor;
}
return nil;
}
3. 完整消息轉(zhuǎn)發(fā)
這一步是消息轉(zhuǎn)發(fā)的最后一步,首先會通過以下兩個方法
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;
- (void)forwardInvocation:(NSInvocation *)anInvocation;
獲得函數(shù)的參數(shù)和返回值,如果返回nil,runtime則會發(fā)出doesNotRecognizeSelector消息,然后crash;
若是返回了一個函數(shù)簽名,runtime就會創(chuàng)建一個NSInvocation對象并發(fā)送- (void)forwardInvocation:(NSInvocation *)Invocation 消息給目標(biāo)對象
#pragma mark - 3、完整消息轉(zhuǎn)發(fā)
- (void)forwardInvocation:(NSInvocation *)anInvocation{
NSLog(@"forwardInvocation");
if ([RuntimeMethodHelper instancesRespondToSelector:anInvocation.selector]) {
[anInvocation invokeWithTarget:_helper];
}
}
/*必須重新這個方法,消息轉(zhuǎn)發(fā)機制使用從這個方法中獲取的信息來創(chuàng)建NSInvocation對象*/
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
NSMethodSignature *signature = [super methodSignatureForSelector:aSelector];
if (!signature){
if ([RuntimeMethodHelper instancesRespondToSelector:aSelector]){
signature = [RuntimeMethodHelper instanceMethodSignatureForSelector:aSelector];
}
}
return signature;
}
示例代碼:
#import "Developer.h"
#import "Finance.h"
#import <objc/runtime.h>
@implementation Developer
- (void)doDeveloper {
NSLog(@"Developer doWork!");
}
+ (BOOL)resolveInstanceMethod:(SEL)sel {
/*
如果當(dāng)前對象調(diào)用了一個不存在的方法
Runtime會調(diào)用resolveInstanceMethod:來進行動態(tài)方法解析
我們需要用class_addMethod函數(shù)完成向特定類添加特定方法實現(xiàn)的操作
返回NO,則進入下一步forwardingTargetForSelector:
*/
/*
class_addMethod(self,
sel,
class_getMethodImplementation(self, sel_registerName("doDeveloper")),
"v@:");
return [super resolveInstanceMethod:sel];
*/
return NO;
}
- (id)forwardingTargetForSelector:(SEL)aSelector {
/*
在消息轉(zhuǎn)發(fā)機制執(zhí)行前,Runtime 系統(tǒng)會再給我們一次重定向的機會
通過重載forwardingTargetForSelector:方法來替換消息的接受者為其他對象
返回nil則進步下一步forwardInvocation:
*/
/*
return [[Finance alloc] init];
*/
return nil;
}
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
/*
獲取方法簽名進入下一步,進行消息轉(zhuǎn)發(fā)
*/
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
/*
消息轉(zhuǎn)發(fā)
*/
return [anInvocation invokeWithTarget:[[Finance alloc] init]];
}