美文网首页
Runtime(三)消息转发

Runtime(三)消息转发

作者: CoderZxq | 来源:发表于2017-05-08 11:46 被阅读0次

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(消息转发和多重继承)

Figure 5-1Figure 5-1

消息转发机制与多重继承:

  • 消息转发模拟多重继承,起到了和多重继承一样的效果。一个对象通过转发机制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中提及。

相关文章

  • runtime底层实现原理

    一、Runtime介绍二、Runtime源码初探三、Runtime消息传递四、Runtime消息转发五、Runti...

  • Runtime(三)消息转发

    Message Forwarding 向一个未处理响应消息的对象发送对应的消息,会发生错误。但是,runtime会...

  • Effective Objective-C读后笔记(2)

    11、runtime消息转发机制 runtime的消息转发流程图消息转发 消息转发的示例实现 这里也给大家推荐一篇...

  • iOS - Runtime - 概念和方法交换

    runtime的概述runtime的相关概念runtime消息机制消息传递动态方法解析消息转发runtime的作用...

  • Runtime窥探 (三)| 消息转发

    前言 其实,重要的不是地图,而是你的方向。所以,每条路都是一次冒险,每个地点都是一场奇遇。 消息转发机制 上回书说...

  • runtime 消息转发

    实例方法的消息传递:消息的转发建立在objc_msgSend(id, SEL, ...)来实现的。首先会在类对象的...

  • Runtime 消息转发

    目录 消息转发背景知识 消息转发使用方式 消息转发常见问题 消息转发背景知识 1.消息转发的定义Objective...

  • Runtime消息转发

    我们还是先从实际代码入手吧,首先,我们先新建一个类,就Person类吧,大家刚学OC的时候用的最多的就是Perso...

  • runtime - 消息转发

    通过前边的学习我们知道,某个类或者对象调某个方法实际上就是给这个类/对象发送消息,如果我们某个对象要调用某个方法,...

  • Runtime - 消息转发

    引言 Objective-C作为iOS开发的主力语言(目前),将来必然被Swift替代。我们都知道在OC中我们是使...

网友评论

      本文标题:Runtime(三)消息转发

      本文链接:https://www.haomeiwen.com/subject/fremtxtx.html