Runtime
First the runtime: in a typical C-based language there is no runtime. The runtime is responsible for all the parts of the language that aren’t complete at compile time, such as where to search a class to instantiate an object or what piece of code to invoke on a method invocation.ISA Pointer
An object instance in memory looks like this: the first 4(8 in a 64 bits world) bytes are the “ISA” pointer, a pointer to the class that instance belongs to. Sometimes when an observer needs to observe attributes of the instance, the ISA pointer points to the observer first and then the real class implementation. Interceptor chain in place.Methods, messages, selectors
So here’s the difference between methods, messages and selectors. A method is an actual piece of code written somewhere in the program. It contains the concrete code, parameters and a return type. A message is what is packaged up when you call a method: target object, parameters and the method name. A selector is an efficient representation of the method name which is used by the runtime to execute quick searches (it uses an integer based representation of the method name instead of a string). You can still call it a method invocation, but before the block of code actually receive control, there are many things going on like routing that message to the correct responder. Since the place where the method call happens doesn’t contain a direct pointer to the method to execute, message-passing seems more appropriate than method call.Methods are C functions
A class also contain all the pointers to the methods included in the class. Essentially is just a search process: search for right place to go to execute the next instruction and route the message there. An interesting fact is that all methods at the end are just regular C functions with 2 extra-parameters: the ‘self’ reference and the selector of the method that was invoked (that can be accessed as _cmd).Message dispatching
When a message is sent to an object instance, the runtime searches a selector to perform the operation. When the selector is not found the runtime delegates the class in three different ways:- resolveInstanceMethod: the runtime first asks the class to add an implementation dynamically. You can reuse a method which has a different name (methodForSelector to retrieve a function pointer). You can also implement the new behavior as a C function (with the 2 additional params) and call class_addMethod to add the new method.
- forwardingTargetForSelector: if no behavior was added in the previous step, the runtime asks the class to forward the message to a different object. The class can return another object and the search starts from scratch.
- forwardInvocation: if no forward was found in the previous step the runtime will package a complete NSInvocation object ( with message, selector, parameters). The class has the last chance to handle the invocation. Something typical is to send the NSInvocation in the NSUndo storage class, so the user can actually undo the operation in case of unexpected results. The class can modify the selector, the parameters, or even re-send the message to multiple objects at once.
My conclusions
forwardInvocation is similar to method_missing in Ruby but the presence of the other two callbacks creates a more structured approach to the message dispatch lifecycle. The main difference seems to be forwardInvocation returns void, so I can potentially accept the message and do nothing acting like /dev/null for that message invocation. I’m absolutely not sure about this, I’d like some Objective-C expert to enlighten me if this is actually possible. The same thing is not possible in Ruby where at least ‘nil’ is always returned by method_missing.Update
Mike Ash was so kind to reply my question on his blog. Here’s the response:“If you do nothing in forwardInvocation: then control will indeed just return to the caller. I don’t know about the return value they will see, though. You may need to explicitly zero out the return value in the NSInvocation before handing it back to them if you don’t want them to receive garbage. Note that you need to have an NSMethodSignature to be able to handle these things, which makes it hard to write an arbitrary message handler. If you’re doing nothing then you can get away with an NSMethodSignature that doesn’t match 100%, but you still need the return type to be correct for the forwarding mechanism to work properly.”
2 years ago