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