美文网首页iOS学习笔记
5.Runtime官方文档学习--消息转发

5.Runtime官方文档学习--消息转发

作者: 郝嗨森 | 来源:发表于2019-01-16 10:56 被阅读0次

    官方文档

    Sending a message to an object that does not handle that message is an error. However, before announcing the error, the runtime system gives the receiving object a second chance to handle the message.

    把消息发送给一个无法处理这个消息的对象是错误的。但是在发生错误之前,运行时系统为接收对象提供了第二次处理消息的机会。

    1.转发

    If you send a message to an object that does not handle that message, before announcing an error the runtime sends the object a forwardInvocation: message with an NSInvocation object as its sole argument—the NSInvocation object encapsulates the original message and the arguments that were passed with it.

    在发生错误之前,如果把消息发送给一个无法处理这个消息的对象,运行时系统会向这个对象发送一个forwardInvocation:消息,其中NSInvacation对象作为其唯一参数。这个NSInvocation对象封装了原始的消息和通过它传递的参数。

    You can implement a forwardInvocation: method to give a default response to the message, or to avoid the error in some other way. As its name implies, forwardInvocation: is commonly used to forward the message to another object.

    你可以实现forwardInvocation:方法给消息提供一个默认的响应,或者其他一些方式起来避免错误。顾名思义,forwardInvocation:通常用来把消息转发给另一个对象。

    To see the scope and intent of forwarding, imagine the following scenarios: Suppose, first, that you’re designing an object that can respond to a message called negotiate, and you want its response to include the response of another kind of object. You could accomplish this easily by passing a negotiate message to the other object somewhere in the body of the negotiate method you implement.

    要查看转发的范围和意图,请设想以下场景:首先,假设你正在设计一个可以响应消息negotiate的对象,而且你想要它的响应包含另一种对象的响应。你可以通过传递一个negotiate消息给你实现的negotiate方法体中的其他对象来轻松完成它。
    个人理解:假设A类和B类都有个negotiate方法,想要在A类响应negotiate消息的同时也让B类响应,则可以在A类的negotiate方法中让B类的对象调用它的negotiate方法就可以了。举例如下:

    // class A
    - (void)negotiate {
        // A类对消息的响应
        NSLog(@"A 类的响应");
        // 让B类也响应
        B *b = [B new];
        [b negotiate]; 
    }
    

    Take this a step further, and suppose that you want your object’s response to a negotiate message to be exactly the response implemented in another class. One way to accomplish this would be to make your class inherit the method from the other class. However, it might not be possible to arrange things this way. There may be good reasons why your class and the class that implements negotiate are in different branches of the inheritance hierarchy.

    更进一步,假设你想要你的对象对negotiate消息的响应完全是在另一个类中实现。实现此目的的一种方法是让你的类继承其他类的方法。然而,这种方式可能无法安排事情。你的类和实现了negotiate的类在继承层次中的不同分支,可能是有充分的原因的。
    个人理解:A类对象收到的消息想要让B类来响应,可以让A继承B,只要A类不重写父类(B类)方法就会自动调用B类的方法来响应。

    Even if your class can’t inherit the negotiate method, you can still “borrow” it by implementing a version of the method that simply passes the message on to an instance of the other class:

    即使你的类不能继承negotiate方法,你仍然可以通过实现一个简单地将消息传递给另一个类的实例的方法版本来“借用”它:

    - (id)negotiate
    {
        if ( [someOtherObject respondsTo:@selector(negotiate)] )
            return [someOtherObject negotiate];
        return self;
    }
    

    This way of doing things could get a little cumbersome, especially if there were a number of messages you wanted your object to pass on to the other object. You’d have to implement one method to cover each method you wanted to borrow from the other class. Moreover, it would be impossible to handle cases where you didn’t know, at the time you wrote the code, the full set of messages you might want to forward. That set might depend on events at runtime, and it might change as new methods and classes are implemented in the future.

    这种做事的方式可能会有点麻烦,特别是如果你希望对象传递给另一个对象的消息有很多。你将不得不实现一个方法来覆盖每一个想从其他类借用的方法。而且,当你写代码时可能无法处理那些你不知道的情况--你想要转发的完整的消息集。该集合可能取决于运行时的事件,而且在将来新的方法和类被实现时可能会改变。

    The second chance offered by a forwardInvocation: message provides a less ad hoc solution to this problem, and one that’s dynamic rather than static. It works like this: When an object can’t respond to a message because it doesn’t have a method matching the selector in the message, the runtime system informs the object by sending it a forwardInvocation: message. Every object inherits a forwardInvocation: method from the NSObject class. However, NSObject’s version of the method simply invokes doesNotRecognizeSelector:. By overriding NSObject’s version and implementing your own, you can take advantage of the opportunity that the forwardInvocation: message provides to forward messages to other objects.

    forwardInvocation:消息提供的第二个机会为这个问题提供了一个不那么特别的解决方案,一种动态的而不是静态的。它这样工作:当一个对象因为没有和消息中的选择器相匹配的方法而无法响应消息时,运行时通过给它发一个forwardInvocation:消息来通知这个对象。每个对象都从NSObject类继承了forwardInvocation:方法。然而,NSObject中这个方法的版本只是调用了doesNotRecognizeSelector:。通过重写NSObject的版本并且实现自己的内容,可以利用forwardInvocation:消息提供的机会转发消息给其他对象。

    To forward a message, all a forwardInvocation: method needs to do is:

    • Determine where the message should go, and
    • Send it there with its original arguments.
      The message can be sent with the invokeWithTarget: method:

    要想转发消息,forwardInvocation:方法所有需要做的是:

    • 确定消息应该去哪里,并且
    • 带着它的原始参数一并发送过去
      发送消息可以通过invokeWithTarget:方法:
    - (void)forwardInvocation:(NSInvocation *)anInvocation
    {
        if ([someOtherObject respondsToSelector:
                [anInvocation selector]])
            [anInvocation invokeWithTarget:someOtherObject];
        else
            [super forwardInvocation:anInvocation];
    }
    

    The return value of the message that’s forwarded is returned to the original sender. All types of return values can be delivered to the sender, including ids, structures, and double-precision floating-point numbers.

    转发的消息的返回值会被返回给原始发送者。所有的返回值类型都可以被传递给发送者,包括id,结构体,双精度浮点数。

    A forwardInvocation: method can act as a distribution center for unrecognized messages, parceling them out to different receivers. Or it can be a transfer station, sending all messages to the same destination. It can translate one message into another, or simply “swallow” some messages so there’s no response and no error. A forwardInvocation: method can also consolidate several messages into a single response. What forwardInvocation: does is up to the implementor. However, the opportunity it provides for linking objects in a forwarding chain opens up possibilities for program design.

    forwardInvocation:方法可一个充当无法识别的消息的分发中心,将他们分配给不同的接收者。或者是一个转发站,把所有消息都发送到同一个目的地。它可以把一条消息翻译成另一条,或者简单地“吞下”一些消息这样既没有响应也没有错误。forwardInvocation:方法还可以将多条消息合并到一个响应中。forwardInvocation:做什么取决于实现。然而,它为转发链中链接对象提供的机会开辟了程序设计的可能性。

    Note: The forwardInvocation: method gets to handle messages only if they don’t invoke an existing method in the nominal receiver. If, for example, you want your object to forward negotiate messages to another object, it can’t have a negotiate method of its own. If it does, the message will never reach forwardInvocation:.

    注意:forwardInvocation:方法只有在他们不调用标称接收者中的现有方法时才处理消息。例如,如果你希望对象去转发negotiate消息给另一个对象,它不可以有自己的negotiate方法。如果它自己有的话,消息将永不会到达forwardInvocation:(自己有的话就直接去自己的方法里了,只有自己无法响应时才去转发)。

    2.转发和多继承

    Forwarding mimics inheritance, and can be used to lend some of the effects of multiple inheritance to Objective-C programs. As shown in Figure 5-1, an object that responds to a message by forwarding it appears to borrow or “inherit” a method implementation defined in another class.

    转发模仿继承,并且可以用于向Objective-C程序提供多继承的一些效果。如下图所示,通过转发来响应消息的对象看起来像是借用或“继承”另一个类中定义的方法实现。


    forwarding.gif

    In this illustration, an instance of the Warrior class forwards a negotiate message to an instance of the Diplomat class. The Warrior will appear to negotiate like a Diplomat. It will seem to respond to the negotiate message, and for all practical purposes it does respond (although it’s really a Diplomat that’s doing the work).

    在这个图示中,Warrior类的实例把一条negotiate消息转发给了Diplomat类的实例。Warrior看上去就像Diplomat一样执行negotiate。它似乎会响应negotiate消息,并且出于所有实际目的它确实响应了(尽管事实上是Diplomat在响应)。

    The object that forwards a message thus “inherits” methods from two branches of the inheritance hierarchy—its own branch and that of the object that responds to the message. In the example above, it appears as if the Warrior class inherits from Diplomat as well as its own superclass.

    转发消息的对象因此从继承层次中的两个分支中继承方法--它自己的分支和响应消息的对象的分支。在上面的例子中,看起来好像Warrior类继承Diplomat类作为它自己的父类。

    Forwarding provides most of the features that you typically want from multiple inheritance. However, there’s an important difference between the two: Multiple inheritance combines different capabilities in a single object. It tends toward large, multifaceted objects. Forwarding, on the other hand, assigns separate responsibilities to disparate objects. It decomposes problems into smaller objects, but associates those objects in a way that’s transparent to the message sender.

    转发提供了你通常希望从多继承中获取的大部分的特性。然而,这两者之间有个很重要的不同:多继承在单个对象中组合了不同的功能。它倾向于大型的,多方面的对象。另一方面,转发将不同的任务分配给不同的对象。它将问题分解为更小的对象,但是通过对消息发送者透明的方式关联那些对象。

    3.代理对象

    Forwarding not only mimics multiple inheritance, it also makes it possible to develop lightweight objects that represent or “cover” more substantial objects. The surrogate stands in for the other object and funnels messages to it.

    转发不仅模仿多继承,还可以开发代表或“覆盖”更多实质对象的轻量级对象。代理代表其他对象并向其传达消息。

    The proxy discussed in “Remote Messaging” in The Objective-C Programming Language is such a surrogate. A proxy takes care of the administrative details of forwarding messages to a remote receiver, making sure argument values are copied and retrieved across the connection, and so on. But it doesn’t attempt to do much else; it doesn’t duplicate the functionality of the remote object but simply gives the remote object a local address, a place where it can receive messages in another application.

    The Objective-C Programming Language中的“远程消息传递”中提到的proxy就是这样的一个代理。proxy负责转发消息给远程接收者的管理细节,通过连接确保参数值被复制和检索,等等。但是他并没有尝试做更多其他的事情;它不会复制远程对象的功能,而只是给远程对象一个可以在另一个程序中接收消息的本地地址。

    Other kinds of surrogate objects are also possible. Suppose, for example, that you have an object that manipulates a lot of data—perhaps it creates a complicated image or reads the contents of a file on disk. Setting this object up could be time-consuming, so you prefer to do it lazily—when it’s really needed or when system resources are temporarily idle. At the same time, you need at least a placeholder for this object in order for the other objects in the application to function properly.

    其他类型的代理对象也是可能的。例如,假设你有一个操作大量数据的对象---也许他会创建一个复杂的图像或者从磁盘上读取文件内容。设置这个对象可能很耗时,因此你更喜欢懒加载它--当真的需要或者系统资源暂时空闲时。同时,你至少需要这个对象的占位符才能让程序的其他对象正常运行。

    In this circumstance, you could initially create, not the full-fledged object, but a lightweight surrogate for it. This object could do some things on its own, such as answer questions about the data, but mostly it would just hold a place for the larger object and, when the time came, forward messages to it. When the surrogate’s forwardInvocation: method first receives a message destined for the other object, it would ensure that the object existed and would create it if it didn’t. All messages for the larger object go through the surrogate, so, as far as the rest of the program is concerned, the surrogate and the larger object would be the same.

    在这种情况下,你可以在初始化时为它创建一个不是完整的对象,而是一个轻量级的代理。这个对象可以自己做一些事情,比如回应关于数据的问题,但大多数情况下,它只是为大的对象保留一个位置,当时机成熟时,把消息转发给它。当代理的forwardInvocation:方法第一次接收到一个发往其他对象的消息时,它会确保这个对象已经存在并且如果不存在就创建它。因此,大的对象的所有消息都通过代理,因此,就其余程序而言,代理和这个较大的对象时相同的。

    4.转发和继承

    Although forwarding mimics inheritance, the NSObject class never confuses the two. Methods like respondsToSelector: and isKindOfClass: look only at the inheritance hierarchy, never at the forwarding chain. If, for example, a Warrior object is asked whether it responds to a negotiate message,

    虽然转发模仿继承,但是NSObject类永远不会混淆这两个。像respondsToSelector:isKindOfClass: 这些方法看起来只在继承层次中,永远不会再转发链中。例如,如果一个Warrior对象被请求是否能响应negotiate消息,

    if ( [aWarrior respondsToSelector:@selector(negotiate)] )
        ...
    

    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.)

    答案是NO,即使在某种意义上,它可以通过把他们转发给Diplomat来毫无错误地接收negotiate消息并且响应它们。

    In many cases, NO is the right answer. But it may not be. If you use forwarding to set up a surrogate object or to extend the capabilities of a class, the forwarding mechanism should probably be as transparent as inheritance. If you want your objects to act as if they truly inherited the behavior of the objects they forward messages to, you’ll need to re-implement the respondsToSelector: and isKindOfClass: methods to include your forwarding algorithm:

    在很多情况下,NO是正确的答案。但也有可能不是。如果你使用转发设置一个代理对象或者扩展一个类的功能,转发机制应该会像继承一样透明。如果你希望对象表现得好像真的继承了它们转发消息的对象的行为一样,你将需要实现方法respondsToSelector:isKindOfClass: 来包含你的转发算法。

    - (BOOL)respondsToSelector:(SEL)aSelector
    {
        if ( [super respondsToSelector:aSelector] )
            return YES;
        else {
            /* Here, test whether the aSelector message can     *
             * be forwarded to another object and whether that  *
             * object can respond to it. Return YES if it can.  */
        }
        return NO;
    }
    

    In addition to respondsToSelector: and isKindOfClass:, the instancesRespondToSelector: method should also mirror the forwarding algorithm. If protocols are used, the conformsToProtocol:method should likewise be added to the list. Similarly, if an object forwards any remote messages it receives, it should have a version of methodSignatureForSelector: that can return accurate descriptions of the methods that ultimately respond to the forwarded messages; for example, if an object is able to forward a message to its surrogate, you would implement methodSignatureForSelector: as follows:

    除了respondsToSelector: and isKindOfClass:,方法instancesRespondToSelector:也应该同样转发算法。如果使用了协议,conformsToProtocol:方法同样应该添加到列表中。同样地,如果一个对象转发任何它收到的消息,它应该实现methodSignatureForSelector:的一个版本来返回最终响应转发消息的方法的准确描述;例如,如果一个对象能够转发消息给它的代理,你应该像下面这样实现methodSignatureForSelector:方法:

    - (NSMethodSignature*)methodSignatureForSelector:(SEL)selector
    {
        NSMethodSignature* signature = [super methodSignatureForSelector:selector];
        if (!signature) {
           signature = [surrogate methodSignatureForSelector:selector];
        }
        return signature;
    }
    

    You might consider putting the forwarding algorithm somewhere in private code and have all these methods, forwardInvocation: included, call it.

    你可能考虑将转发算法放在私有代码的某个地方,并让所有方法包括forwardInvocation:调用它。

    Note: This is an advanced technique, suitable only for situations where no other solution is possible. It is not intended as a replacement for inheritance. If you must make use of this technique, make sure you fully understand the behavior of the class doing the forwarding and the class you’re forwarding to.

    注意:这是一项高级技术,只适用于无法提供其他解决方案的情况。它不是继承的替代品。如果你一定要使用这项技术,确定你完全理解了执行转发的类和你要转发去的类的行为。

    The methods mentioned in this section are described in the NSObject class specification in the Foundation framework reference. For information on invokeWithTarget:, see the NSInvocation class specification in the Foundation framework reference.

    本节中提到的方法在Foundation框架参考中的 NSObject 类规范中进行了描述。 有关invokeWithTarget:的信息,请参阅Foundation框架参考中的 NSInvocation 类规范。

    相关文章

      网友评论

        本文标题:5.Runtime官方文档学习--消息转发

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