Runtime(三)消息转发

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-1
Figure 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中提及。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容