So i am looking into message forwarding and running some unit tests when i came across some scant resources including apple's very own brand of documentation of using forwardInvocation:
which as far as i can tell requires methodSignatureForSelector:
to work.
now i get the general idea that methodSignatureForSelector:
is required to see if the object you are trying to forward the message to has the matching method name and parameters so it can then call forwardInvocation:
my question is why in apples documentation it says to call the superclass's implementation of methodSignatureForSelector:
like so...
- (NSMethodSignature*)methodSignatureForSelector:(SEL)selector
{
NSMethodSignature* signature = [super methodSignatureForSelector:selector];
if (!signature) {
signature = [surrogate methodSignatureForSelector:selector];
}
return signature;
}
to me this looks like it's saying "If none of the classes I have inherited from have a way to handle this method, check to see if surrogate object does."
The example apples give is the Warrior acting in stead of the Diplomat when it comes to the method of negotiating. Given this example I do not see why you would check to see if warrior or any of it's parents ever have the appropriate method signature to forward to. So this leads me to believe it is there for another reason, one which i can't think of, can someone give me an example or help clear up where i might be missing the point?
TL;DR
why would I need [super methodSignatureForSelector:selector];
?
You are correct - the example tries to get the method signature from the superclass, and if it cannot, it asks the surrogate for one. In that whole Forwarding and Inheritance section, Apple is guiding you in how it intends you to use message forwarding - whether you choose to listen is up to you :)
To fully, explain, I will walk through the main portions of the documentation:
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.
Implementing the forwarding of messages does not immediately affect the respondsToSelector
and isKindOfClass
methods. if you forward negotiate
to a surrogate, if you call [myWarrior respondsToSelector:negotiate]
will return NO
if the method does not exist in the inheritance hierarchy.
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.
The keyword is probably - so Apple is giving you a recommendation. Apple is stating that if you want the myWarrior object to return YES
in my example above since you forward negotiate
to a surrogate object, then you need to overwrite the respondsToSelector
method. Now note though there are other methods besides negotiate
that you can call, potentially in the surrogate that you do not wish to return YES
. For example, the Diplomat class might have a havePeaceCelebration
method. When the Warrior class gets sent this message, you might not have implemented the forwarding of the message to the Diplomat class (since Warriors do not have peace celebrations) and therefore you will want to return NO
.
Moreover, the parent class might have a chooseWeapon
method not in the Warrior class. If you call [myWarrior respondsToSelector:chooseWeapon]
you most definitely want to check if the superclass responds to it since the surrogate (being a Diplomat) does not.
Lastly, it is possible that both the parent class and the surrogate respond to the selector. Apple seems to recommend that the parent class should win out - a Warrior is a Warrior first, and a Diplomat for some methods, only if you force it to be. How you end up implementing it though is up to you.
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:
The keyword is should - again a recommendation. This was the statement right before the code you provided. This is identical reasoning to respondsToSelector
, and is saying that the object should be a good citizen. It may be possible for the Warrior class to handle some remote messages, but not others, and same for the Diplomat class. If you choose to always forward it to the surrogate, it might be confusing if messages are forwarded that Warrior's superclass can handle. Or worse, it might forward messages that Warrior's superclass can handle but the surrogate cannot - potentially resulting in exceptions.