mikeash.com: just this guy, you know?

Posted at 2009-03-27 16:50 | RSS feed (Full text feed) | Blog Index
Next article: Friday Q&A 2009-04-03: On Hold
Previous article: Friday Q&A 2009-03-20: Objective-C Messaging
Tags: fridayqna objectivec
Friday Q&A 2009-03-27: Objective-C Message Forwarding
by Mike Ash  
This article is also available in Chinese (translation by neoman).

Welcome back to another exciting Friday Q&A. This week I'm going to continue the series on the Objective-C runtime. Yuji Tachikawa suggested talking about how @dynamic properties work in CoreData and I'm going to take that and expand it to talk about message forwarding in general.

No Such Method
Last week I talked about how Objective-C messaging works, and mentioned that interesting things happen when no method is found for a given selector. Those interesting things are what make forwarding happen.

(If you aren't totally clear on what a selector is or what the difference is between a method and a message, you might want to go read through that article real quick, or at least re-read the Definitions section if you already read it.)

Just what is message forwarding? Simply speaking, it allows unknown messages to be trapped and reacted to. In other words, any time an unknown message is sent, it gets delivered to your code in a nice package, at which point you can do whatever you like with it.

This kind of thing is incredibly powerful and allows for doing all kinds of nifty, clever things.

Right about now, you're probably wondering, "Why is it called forwarding?" There doesn't seem to be much of a link between taking arbitrary actions in response to unknown messages, and "forwarding". The reason for this is because this technique was mainly intended to allow objects to let other objects handle the message for them, thus "forwarding".

What Happens
What happens when you do [foo bar] and foo doesn't implement a bar method? When it does implement such a method, it's pretty straightforward: it looks up the appropriate method, then jumps to it. When no such method can be found, a complicated sequence of events ensues:

  1. Lazy method resolution. This is done by sending resolveInstanceMethod: (resolveClassMethod: for class methods) to the class in question. If that method returns YES, the message send is restarted under the assumption that the appropriate method has now been added.
  2. Fast forwarding path. This is done by sending forwardingTargetForSelector: to the target, if it implements it. If it implements this method and it returns something other than nil or self, the whole message sending process is restarted with that return value as the new target.
  3. Normal forwarding path. First the runtime will send methodSignatureForSelector: to see what kind of argument and return types are present. If a method signature is returned, the runtime creates an NSInvocation describing the message being sent and then sends forwardInvocation: to the object. If no method signature is found, the runtime sends doesNotRecognizeSelector:.

Lazy Resolution
As we learned last week, the runtime sends messages by looking up a method, or IMP, and then jumping to it. Sometimes it can be useful to dynamically plug IMPs into a class instead of setting them all up beforehand. Doing this allows for really fast "forwarding", because after the method is resolved, it gets invoked as part of the normal message sending process. The disadvantage is, of course, that this isn't very flexible, since you need to have an IMP ready to plug in, and that in turn means that you need to have already anticipated the argument and return types that will be arriving.

This kind of thing is great for stuff like @dynamic properties. The method signature is something you should know in advance: you'll either take one parameter with a void return, or have no parameters and return one value. The types of the values will vary, but you can cover the common cases. Since the IMP gets passed the selector that's been sent to the object, it can use that selector to get the name of the property and look it up dynamically. Plug it in to the class using +resolveInstanceMethod: and off you go.

Fast Forwarding
The next thing the runtime does is see if you want to just send the whole message unchanged to a different object. Since this is a common case of forwarding, this allows it to be done with minimal overhead.

For some reason, fast forwarding is really poorly documented. The only place Apple even mentions it, aside from a commented-out declaration in NSObject.h, is in the Leopard release notes. (Search for "New forwarding fast path".)

This technique is great for faking multiple inheritence. You can write a little override like this:

    - (id)forwardingTargetForSelector:(SEL)sel { return _otherObject; }
This will cause any unknown message to be sent to _otherObject, which will make your object appear from the outside as though it combined your object with this other object in one.

Normal Forwarding
The first two are basically just optimizations that allow forwarding to go faster. If you don't take advantage of them, the full forwarding mechanism goes into action. This creates an NSInvocation object which fully encapsulates the message being sent. It holds the target, the selector, and all of the arguments. It also allows full control over the return value.

Before the runtime can build the NSInvocation it needs an NSMethodSignature, so it requests one using -methodSignatureForSelector:. This is required due to Objective-C's C heritage. In order to bundle the arguments up in the NSInvocation, the runtime needs to know what kind of arguments there are, and how many of them there are. This information isn't normally provided in a C runtime environment, so it has to do an end run around the C "bag of bytes" view of the world and get that type information in another way.

Once the invocation is constructed, the runtime then invokes your forwardInvocation: method. From there you can do whatever you want with the invocation it hands you. The possibilities are endless.

Here's one quick example. Imagine you're tired of writing loops, so you want to be able to manipulate arrays more directly. Add this little category to NSArray:

    @implementation NSArray (ForwardingIteration)
    
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)sel
    {
        NSMethodSignature *sig = [super methodSignatureForSelector:sel];
        if(!sig)
        {
            for(id obj in self)
                if((sig = [obj methodSignatureForSelector:sel]))
                    break;
        }
        return sig;
    }
    
    - (void)forwardInvocation:(NSInvocation *)inv
    {
        for(id obj in self)
            [inv invokeWithTarget:obj];
    }
    
    @end
Then you can write code like this:
    [(NSWindow *)windowsArray setHidesOnDeactivate:YES];
I don't recommend writing code like this. The trouble is that forwarding won't catch any methods already implemented by NSArray, so you'll end up being able to capture some but not others. A much better approach is to write a trampoline class by subclassing NSProxy.

NSProxy is basically a class that's expilicitly designed for proxying. It implements a minimal subset of methods, leaving everything else up for grabs. This means that a subclass that implements forwarding can capture basically any message.

To use NSProxy for this kind of thing, you'd write an NSProxy subclass that can be initialized to point at an array, and then add a little stub method to NSArray that returns a new instance of the proxy, like so:

    @implementation NSArray (ForwardingIteration)
    - (id)do { return [MyArrayProxy proxyWithArray:self]; }
    @end
Then you'd use it like this:
    [[windowsArray do] setHidesOnDeactivate:YES];
This whole area of writing trampolines to capture messages and have them do interesting things has been well explored and has been given the name Higher-Order Messaging. I won't go into more detail about it in this post, but there's a lot of neat stuff out there.

Declarations
Another consequence of Objective-C's C heritage is that the compiler needs to know the full method signature of every message that you're going to send in your code, even purely forwarded ones. To make a contrived example, imagine writing a class that uses forwarding to produce integers from code, so that you can write this:

    int x = [converter convert_42];
This is obviously not very useful, but you could certainly do it. More useful variants of this technique are possible.

The trouble is that the compiler doesn't know about any convert_42 method, so it has no idea what kind of value it returns. It will give you a nasty warning, and will assume that it returns id. The fix to this is simple, just declare one somewhere:

    @interface NSObject (Conversion)
    - (int)convert_42;
    - (int)convert_29;
    @end
Again, this obviously isn't very useful to do, but in cases where you have a more practical forwarding situation, this can help you make peace with the compiler. For example, if you use forwarding to fake multiple inheritence, use a category to declare all of the methods of the other class as applying to the multiply-inheriting class as well. That way the compiler knows that it has both sets of methods. One set gets handled by forwarding, but that doesn't matter to the compiler.

Conclusion
Message forwarding is a powerful technique that greatly multiplies the expressiveness of Objective-C. Cocoa uses it for things like NSUndoManager and distributed objects, and it can let you do a lot of nifty things in your own code.

That wraps up this week's Friday Q&A. Tune in next week for more riveting tales of programming, and leave your comments on this edition below.

Friday Q&A is powered by the contribution of your ideas. If you have an idea you'd like to see discussed here, post it in the comments or e-mail it directly. I will use your name unless you ask me not to.

Did you enjoy this article? I'm selling whole books full of them! Volumes II and III are now out! They're available as ePub, PDF, print, and on iBooks and Kindle. Click here for more information.

Comments:

Message forwarding is a great feature of Objective-C, and lets you do some bizarre but useful stuff. One way I've used runtime proxies is in building an object editor with OK/cancel.

Let's say that, in my app, the user wants to edit one of his Whizbang objects. In my editing controller, I'll create one of these proxy objects, and point it at the Whizbang he's editing. Then, I bind my interface to the proxy object, not the Whizbang. The editor proxy will then use KVC/KVO to "pull through" values from the Whizbang. If the user makes any changes, those are stored within the proxy. If the user clicks Cancel, those values are blown away. If the user clicks OK, the proxy iterates through its changes and sets the values on the Whizbang.

This eliminates glue code, ties in automatically with the undo manager, etc. It's fantastic.
Steve, that's a great idea.

Anyone else tried something similar? I'm interested in any experiences with that approach.

Great article btw.
Mike, your posts continue to be great. I would never have seen forwardingTargetForSelector: if you hadn't pointed it out.

Sijmen, there are lots of common code examples that use the method forwarding system to record methods in NSInvocations (to "forward" them later).

The NSUndoManager does this to record undo invocations. I wrote a post a while ago on how you can do this in your own code: http://cocoawithlove.com/2008/03/construct-nsinvocation-for-any-message.html
Is there any portable way to generate NSMethodSignatures? You can get one using a type encoding[1], but I'm under the impression Apple doesn't guarantee that the type encoding will always stay the same.

[1] http://developer.apple.com/DOCUMENTATION/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html
The best way to generate an NSMethodSignature is to get one with an NSObject method. Generally you will know what kind of signature you want when you write the code, so you can just write a little stub method (if you don't have a "real" one available) and then ask the object or class for the method signature for that method.

I don't see where Apple says that type encodings can change. Generally, if they give a list like that, they mean for it to be something you can rely on. More concretely, since these strings get generated by the compiler and hardcoded into the binary, it would be impossible for them to make changes without destroying all sorts of existing stuff.
What method do you think apple use in the @dynamic properties declaration in the CoreData framework?
To my knowledge, @dynamic uses lazy method resolution with a great deal of internal trickery which allows it to bypass the slower but easier-to-deal-with normal forwarding path.
Thanks for a great article Mike! forwardingTargetForSelector is very useful for the normal forwarding case, and I'm using it now to insert a delegate object to handle normal behaviour and pass on unused delegate messages to a user supplied delegate.

One thing to clarify is that you also need to implement respondsToSelector, especially in the case of a delegate where the methods safe generally optional. If you don't implement respondsToSelector, the caller will not try to execute the forwarded methods.
Hi!

Is there any way to forward class message to object? For example
+ [MyClass test] should be forwarded to - [someObject test].

What i'm actually trying to implement is singleton with useful class methods declared in protocol, and if user send class message it should be forwarded to shared instance.

I believe you can simply implement the forwarding methods as class methods and they will work to intercept messages sent to the class.
Thank you, Mike!
I've actually did it when read about meta-class, just after i asked this question. Objective-C doesn't stop to surprise me!
Mike, that is a very helpful post.
I put together a proxy class implementation for iOS 6.0 SLComposeViewController class. It demonstrates a complete proxy code that forwards both instance and class methods and allows to use the new API on earlier versions of iOS.
https://github.com/dmitrikozlov/MJSocialComposeViewController

Hopefully someone will find it useful.

Comments RSS feed for this page

Add your thoughts, post a comment:

Spam and off-topic posts will be deleted without notice. Culprits may be publicly humiliated at my sole discretion.

Name:
The Answer to the Ultimate Question of Life, the Universe, and Everything?
Comment:
Formatting: <i> <b> <blockquote> <code>.
NOTE: Due to an increase in spam, URLs are forbidden! Please provide search terms or fragment your URLs so they don't look like URLs.
Code syntax highlighting thanks to Pygments.
Hosted at DigitalOcean.