如果你給一個對象發(fā)送它不認(rèn)識的消息時社痛,系統(tǒng)會拋出一個錯誤胀蛮,但在錯誤拋出之前蹂析,運(yùn)行時會給改對象發(fā)送forwardInvocation:消息,同時傳遞一個NSInvocation對象作為該消息的參數(shù)谴轮,NSInvocation對象包封裝原始消息和對應(yīng)的參數(shù)炒瘟。你可以實(shí)現(xiàn)forwardInvocation:方法來對不能處理的消息做一些默認(rèn)的處理,以避免程序崩潰第步,但正如該函數(shù)的名字一樣疮装,這個函數(shù)主要是用來將消息轉(zhuǎn)發(fā)給其它對象。每一個對象都從NSObject類繼承了forwardInvocation:方法粘都,但在NSObject中廓推,該方法只是簡單的調(diào)用doesNotRecognizeSelector:,通過重寫該方法你就可以利用forwardInvocation:將消息轉(zhuǎn)發(fā)給其它對象翩隧。
??為了轉(zhuǎn)發(fā)一個消息樊展,forwardInvocation:需要做的事:
- 確定消息該往哪發(fā)
- 同時發(fā)送對應(yīng)的原始參數(shù)
消息可以通過invokeWithTarget:方法發(fā)送:
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
if ([someOtherObject respondsToSelector:[anInvocation selector]])
[anInvocation invokeWithTarget:someOtherObject];
else
[super forwardInvocation:anInvocation];
}
forwardInvocation:方法可以作為無法認(rèn)識的消息的分發(fā)中心,將它們打包發(fā)給不同的接受者堆生,或者作為一個中轉(zhuǎn)站將所有消息發(fā)往同一個目的地专缠。它可以將一個消息轉(zhuǎn)換成另外的一個消息,或者只是單純的把消息“吞下”淑仆,不對外響應(yīng)和拋出錯誤涝婉,forwardInvocation:做什么取決于實(shí)現(xiàn)者。
轉(zhuǎn)發(fā)和多繼承
轉(zhuǎn)發(fā)有點(diǎn)像模仿繼承蔗怠,一個對象通過轉(zhuǎn)發(fā)響應(yīng)一個消息墩弯,看起來像是繼承了其它類實(shí)現(xiàn)的方法吩跋。
如上圖所示,一個Warrior類的實(shí)例轉(zhuǎn)發(fā)一條negotiate消息到Diplomat類的實(shí)例渔工,使得Warrior看起來能夠像Diplomat一樣進(jìn)行negotiate锌钮。
通過轉(zhuǎn)發(fā)消息的對象從而從兩個繼承體系分支“繼承”了方法:原本的繼承分支和通過響應(yīng)消息。在上面的例子中引矩,Warrior看起來好像繼承自Diplomat和它的父類梁丘。然而轉(zhuǎn)發(fā)跟繼承有一個重要的區(qū)別:繼承將不同的功能集合到一個對象;而轉(zhuǎn)發(fā)是將不同的功能分配給不同的對象旺韭,它將一個問題分解成小的對象兰吟,然后通過一種對消息發(fā)送者透明的方式把這些對象關(guān)聯(lián)起來。
雖然轉(zhuǎn)發(fā)能夠模仿繼承茂翔,但是NSObject類不會因?yàn)檫@兩個困惑,類似 respondsToSelector: 和 isKindOfClass: 方法只會查看它的繼承體系履腋,不會去查看轉(zhuǎn)發(fā)鏈珊燎,比如下面查看Warrior能否響應(yīng)negotiate消息:
if ( [aWarrior respondsToSelector:@selector(negotiate)] )
...
答案永遠(yuǎn)是NO,即便它能夠通過將該消息轉(zhuǎn)發(fā)給Diplomat而響應(yīng)該消息且不會拋出錯誤遵湖。
??同時在調(diào)用forwardInvocation:之前悔政,需要先調(diào)用methodSignatureForSelector:獲取指定selector的方法簽名:
- (NSMethodSignature*)methodSignatureForSelector:(SEL)selector
{
NSMethodSignature* signature = [super methodSignatureForSelector:selector];
if (!signature) {
signature = [surrogate methodSignatureForSelector:selector];
}
return signature;
}
示例:
@interface ForwardClass : NSObject
-(void)doSomethingElse;
@end
@implementation ForwardClass
-(void)doSomethingElse
{
NSLog(@"doSomething was called on %@", [self class]);
}
@end
@interface SomeClass : NSObject
{
id forwardClass;
}
-(void)doSomething;
@end
@implementation SomeClass
-(id)init
{
if (self = [super init]) {
forwardClass = [ForwardClass new];
}
return self;
}
-(void)doSomething
{
NSLog(@"doSomething was called on %@", [self class]);
}
-(void)forwardInvocation:(NSInvocation *)invocation
{
if (! forwardClass) {
[self doesNotRecognizeSelector: [invocation selector]];
}
[invocation invokeWithTarget: forwardClass];
}
-(NSMethodSignature*)methodSignatureForSelector:(SEL)selector
{
NSMethodSignature *signature = [super methodSignatureForSelector:selector];
if (! signature) {
//生成方法簽名
signature = [forwardClass methodSignatureForSelector:selector];
}
return signature;
}
@end
現(xiàn)在給SomeClass對象發(fā)送doSomethingElse消息后程序不會crash了:
id someClass = [SomeClass new];
[someClass doSomething];//ForwardTest[1291:56187] doSomething was called on SomeClass
[someClass doSomethingElse];// ForwardTest[1291:56187] doSomething was called on ForwardClass
消息轉(zhuǎn)發(fā)有很多的用途,比如:
- 創(chuàng)建一個對象負(fù)責(zé)把消息轉(zhuǎn)發(fā)給一個由其它對象組成的響應(yīng)鏈延旧,代理對象會在這個有其它對象組成的集合里尋找能夠處理該消息的對象谋国;
- 把一個對象包在一個logger對象里,用來攔截或者紀(jì)錄一些有趣的消息調(diào)用迁沫;
- 比如聲明類的屬性為dynamic芦瘾,使用自定義的方法來截取和取代由系統(tǒng)自動生成的getter和setter方法。