Message Forwarding
向一个未处理响应消息的对象发送对应的消息,会发生错误。但是,runtime会给这个对象第二次机会来处理这个message,避免发生错误。
Forwarding(消息转发)
向一个未处理响应消息的对象发送对应的消息,在通知错误前,runtime会向receiving object发送一个带有NSInvocation的参数的forwardInvocation:消息,这个NSInvocation参数包装了最初的消息(origin message)和参数。
你可以实现forwardInvocation:,给一个默认恢复(default response)或用其他方法阻止错误通知。正如其名字暗示forwardInvocation:将消息转发给其他对象。
为了验证转发(forwarding)的范围和意图,想象一下:首先,你创建了一个对象能够响应negotiate,并希望自己的响应(response)包含其它对象的响应。你在negotiate的实现中,把negotiate消息发送给其他对象。
更近一步:让自己对negotiate的响应由另一个类的实现去响应。一种方法是让你的类继承另一个类的方法,然后这是不可能的。因为不同的类可能在继承结构中的不同分支上。
尽管你不可以继承另一个类的方法,但是你仍然可以借它(borrow it):把message发送给其他对象。
- (id)negotiate {
if ([someObject responseTo:@selector(negotiate)]) {
return [someObject negotiate];
}
return self;
}
以上方法可能有一些笨重:你需要实现方法去覆盖所有你借的方法,对于整个消息集合(the full set of messages)你可能覆盖不了。再一个,在运行期,这些消息集合可能会改变的。
forwardInvocation:可以减少实现方法,并且是动态的。它是这样工作的:当一个object不可以response一个message的时候,runtime会发送forwardInvocation:message给object。每一个object继承NSObject的forwardInvocations:,可以重写(override)将messages发送给其他对象。NSObject的forwardInvocation:简单的调用doesNotRecognizeSelector:。
- (void)forwardInvocation:(NSInvocation *)anInvocation {
if ([someOtherObject responseSelector:[anInvocation selector]])
[anInvocation invokeWithTarget:someOtherObject];
else
[super forwardInvocation:anInvocation];
}
被转发的消息的返回值,会被返回最初的发送者(sender)
forwardInvocation:有这么几个角色:
- 未识别消息(unrecognized messages)的分发中心(distribution center),打包messages给接受者(receiver)。
- 或者是一个转移站(transfer station),把所有messages发送给相同的目的地。
- 或者是把一个message转换为另一个message,或者简单的“吞咽”(swallow)一些messsages,所以没有response和error。
- 或者把多个消息合并(consolidate)为一个响应。
forwardInvocation:能做什么取决于(up to)实现者(implementor)。
note:只有在不能调用名义上的(nominal)receiver的存在的方法,forwardInvocation:才起作用。如果你自己可以实现响应的方法,message永远不会到达forwardInvocation:
如果想了解更多forward,invocation,可以参考NSInvocation规格说明。
Forwarding and Multiple Inheritance(消息转发和多重继承)
消息转发机制与多重继承:
- 消息转发模拟多重继承,起到了和多重继承一样的效果。一个对象通过转发机制response to message,就像“borrow”或“inherit”其他类定义的方法。
- 本来object1要响应的message,通过borrow object2的方法来响应message,仿佛(seem)是自己响应一样。
- object1和object2处在继承结构(inheritance hierarchy)的两个分支(two branches)上,消息响应让object1看起来像是继承自object2一样。
- 消息转发机制虽然提供了多重继承的大部分功能,但是两者仍然有一些重要的不同。多重继承把不同的能力结合到一个单一对象中,趋向于更大,更多面的对象;另一方面,消息转发把问题分解成更小的对象(smaller objects),但是能够以一种对发送方(sender)透明的方式把这些对象(objects)联系在一起。
Surrogate Objects(代理对象)
消息转发不仅仅模拟多重继承,它也可以创建轻量级的对象来代表(reperent)和覆盖(cover)实质的(substantial)对象。代理人(surrogate)作为其他对象的代理,并把消息几种发给它。
假设一下:你有一个object要处理大量的数据,比如:处理图片,或读取文件在磁盘上的内容。建立这个对象是很耗时的,所以你更希望在需要它的时候,或系统不忙的时候创建它。但同时,你又需要它作为占位符,以便程序中的其他对象可以正常工作。
在这种情况下,你初始化一个轻量级的代理(lightweight surrogate),而不是一个完整的object。当时间到来,把messages转发给surrogate。
Forwarding and Inheritance(消息转发和继承)
尽管forwarding模拟Inheritance,但是NSObject从来不会混淆(confuse)它们。方法例如:responseSelector:和isKindOfClass:只会在Inheritance hierarchy查找,不会在消息转发链中(forwarding chain)查找。
a Warrior object is asked whether it responds to negotiate message,the answer is NO, even though it can receive negotiate messages without error and respond to them, in a sense, by forwarding them to a Diplomat. (See Figure 5-1.)
if ([aWarrior responseToSelector:@selector(negotiate)])
...
在很多情况下,NO是正确的答案。但是,它也可能不是。如果你想通过forwarding去创建(set up)一个代理对象(surrogate object)
,或扩展(extend)一个类的能力(capabilities),消息转发机制(forwarding mechanism)应该像hierarchy那样透明(transparent)。如果你想让你的objects看起来继承了其他objects(收到转发消息的objects)的行为,你需要重写responseToSelector:和isKindOfClass:,方法中含转发的规则(forwarding algorithm)。
- (BOOL)responseToSelector:(SEL)selector {
if ([super responseToSelector:selector]) {
return YES;
} else {
/* Here, test whether the selector message can
be forwarded to another object and whether that
object can respond to it. Return YES if it can.
*/
}
}
除了responseToSelector:和 isKindOfClass:,instancesRespondToSelector:也应该反映(mirror)到forwarding algorithm。如果使用了协议(protocols),conformsToProtocol:也应该加入到列表里。类似,an object转发收到的remote messages,methodSignatureForSelector:要返回这个方法的描述。
- (NSMethodSignature*)methodSignatureForSelector:(SEL)selector
{
NSMethodSignature* signature = [super methodSignatureForSelector:selector];
if (!signature) {
signature = [surrogate methodSignatureForSelector:selector];
}
return signature;
}
你可以把forwarding algorithm放在私密代码里。
note:这是很先进的(advanced)技术(technique),但是当其他方法无法解决问题的时候,再使用它。forwarding mechanism并不打算代替inheritance。如果你确定使用该技术时,你一定要完全理解 the behavior of the class doing the forwarding and the class you’re forwarding to.
这一节提及到的方法,在NSObject规范(specification)提及。invokeWithTarget:在NSInvocation中提及。