Hmmm, What's that Selector?

The journey of a selector that is unrecognized.

Introduction

The atomic unit in Objective-C is an invocation, sending a selector to an object with some arguments. To invoke the selector doThings: on anObject we write the familiar line.

[anObject doThings:things];

Usually this calls into the method and all is well. But what happens when anObject doesn't have a method implementation for doThings?

An adventure begins where we'll meet the forwarding path, proxy objects, zombies, and some magic that is arguably witchcraft.

Buckle up; here we go. First stop, invocations!

Selector-Invocation

Objc-Message-Send, the Invoker Behind the Curtain

Invocations in Objective-C are compiled into a call to the C function objc_msgSend.

[anObject doThings:things];

// Compiled as:
objc_msgSend(anObject, @selector(doThings:), things);

objc_msgSend simply finds the imp and calls it. In this case the imp, short for implementation, is the function pointer corresponding to @selector(doThings:) in the method table for the class of anObject.

id objc_msgSend(id self, SEL _cmd, ...) {
  Class class = object_getClass(self);
  IMP imp = class_getMethodImplementation(class, _cmd);
  return imp ? imp(self, _cmd, ...) : 0;
}

objc_msgSend is actually written in assembly in order to properly handle any or no return type as well as properly load all arguments before calling the imp.

Class-Get-Method-Implementation, the Function Finder

The implementation of class_getMethodImplementation is quite simple. It calls lookupMethod which searches for the given selector in the method table of the class. If no method is found then it walks up the inheritance hierarchy looking for it. Each class owns its imp-cache and there is a path for resolving methods, a form of dynamic creation for instance and class methods, but don't let that make you think that method-lookup is complicated. It's just a linear search of an array of methods or binary search when they are sorted and then a walk up the hierarchy.

Runtime Forwarding

Objc-Message-Forward

What's interesting about the pseudo-implementation about is that  when all the caches and method lists fall through. in class_getMethodImplementation In that case it returns _objc_msgForward_internal which is then translated into a pointer to the function _objc_msgForward.  Thus when there is no method for a given selector objc_msgSend will end up calling objc_msgForward.

It and its partner in crime _objc_msgForward_stret (for methods with a struct return) are public and declared in <objc/message.h> but you have no reason to call them directly, so stop thinking about it.

If you are absolutely insane interested you can read the implementation of _objc_msgForward.

It is implemented separately for arm, i386x86-64, and a simulator-specific i386 version as well. Be warned, they are all written in assembly and quite dense!

Forwarding Handlers

The two forwarding methods described just call into the two forwarding handlers that have been specified at runtime using objc_setForwardHandler.

// <objc/runtime.h>
void objc_setForwardHandler(void *fwd, void *fwd_stret);

You are unlikely to ever set any forwarding handlers yourself but guess who does... Core Foundation!

Core Foundation's Forwarding Path

Preparation

When Core Foundation is first loaded by dyld its runs __CFInitialize as a static initializer in which it sets __forwarding_prep_0__ as the srandard forwarding handler and the nearly identical __forwarding_prep_1__ for the struct return (stret) variant. What's interesting is that the setting for the forwarding handlers, a call to objc_setForwardHandler, is missing from the CF source, yet the call is clearly visible if the framework is disassembled. There be gremlins!

Preamble

Both of the __forwarding_prep_X__ functions just call __forwarding__ function with two arguments. The first is a boolean denoting whether the return type is a struct or not. A zero is passed for __forwarding_prep_0__ and a one for __forwarding_prep_1__; at this point their names should be obvious. The second argument is the current stack pointer which is used to find the arguments that were passed to objc_msgSend, the first two of which are always self and _cmd which make for easy retrieval at the start of the method.

void __forwarding__(BOOL isStret, void *frameStackPointer, ...) {
  id receiver = *(id *)frameStackPointer;
  SEL sel = *(SEL *)(frameStackPointer + 4);

  ...
}

For brevity's sake the rest of the discussion will act as if receiver and sel are actually arguments to __forwarding__, but let the pedants sleep well knowing that I have addressed the fact that in reality they are not.

The Forwarding Path

The Setting

Let's recall our starting point. A selector is invoked on an object that does not "recognize" it and then the run-time calls the __forwarding__ function for the chance for salvation. Clearly it doesn't just crash or we would be in brutally brittle world where the simplest mistake would bring down an application and where this post would have been better served to the bit-bucket.

Let's dive down this quirky rabbit hole and see where we end up. It's time to move forward.

Forwarding Target, the Replacement Receiver

So here we are in the middle of objc_msgSend and we've hit a wall. We've got the

  • who - receiver, 
  • the what - selector,
  • and the with - arguments

but we can't quite make it happen. The easiest and fastest resolution would be to replace the receiver and then invoke the same selector with the same arguments. If there were such a replacement receiver then all that would need to happen would be to undo the call (unwind the stack and return the registers to as they were at the start of the stack frame), replace the first argument (a single stack- or register-write), and then call objc_msgSend again. It's such a quick and clean recovery!

The run-time offers the chance to return a replacement receiver, called the forwarding target, by calling the obviously named forwardingTargetForSelector: on the original receiver. If it doesn't return nil then the runtime invokes the selector on the returned object, the forwarding path is complete, and with a swift recovery life goes on.

void __forwarding__(BOOL isStret, id receiver, SEL sel, ...) {
  id forwardingTarget = [receiver forwardingTargetForSelector:sel];
  if (forwardingTarget) {
    return objc_msgSend(forwardingTarget, sel, ...);
  }

  ...
}

Note that if for some reason the selector is not recognized by the forwarding target then the forwarding path would start all over again but with the new receiver. If this actually happens then you are likely doing something wrong. Why on Earth would you tell the runtime to call the selector on an object that doesn't it? Don't be so cruel. Go play with fire instead.

Method Signature, the Frame-Forge

When forwardingTargetForSelector: does return nil the forwarding path fills into the slow branch. Here it will box up everything about the current invocation and hand it to you to do whatever with you please.

Here lies an interesting question: how, in the middle of an invocation, is it able to package up the target, selector, and all arguments, when they could easily be scattered throughout the registers or the stack (the exact details of which depend on calling conventions of the given architecture and compiler).

Grabbing all the arguments is simply not possible with a selector alone. The class does not implement the selector and thus the method signature, which includes all of type information, of the invocation is unknown. If we had the method signature for the given selector it would be possible to determine exactly where all of the arguments are and then wrap them up into an NSInvocation. It attempts to ascertain the signature by calling the aptly named methodSignatureForSelector: on the receiver.

void __forwarding__(BOOL isStret, id receiver, SEL sel, ...) {
  id forwardingTarget = [receiver forwardingTargetForSelector:sel];
  if (forwardingTarget) {
    return objc_msgSend(forwardingTarget, sel, ...);
  }

  NSMethodSignature *methodSignature = [receiver methodSignatureForSelector:sel];
  ...
}

Forward Invocation, the Everything; the End

If the receiver is able to construct and return method signature for the given selector then the last stop in the forwarding path is forwardInvocation: which is called with an NSInvocation made by scraping the stack and registers using that signature.

In forwardInvocation: you can do anything that you want including logging the invocation, changing any of the arguments or the receiver before invoking, forwarding it onto other objects (multiplexing), or simply setting the invocation's return value directly.

You should note that even if you do nothing with the invocation, its return value (defaulting to nil, Nil, NULL, 0, 0.0, the abyss, etc.) will still be loaded as the return value of the invocation and it will look as if objc_msgSend returned it.

You didn't forget that we're in the middle of objc_msgSend, did you?

void __forwarding__(BOOL isStret, id receiver, SEL sel, ...) {
  id forwardingTarget = [receiver forwardingTargetForSelector:sel];
  if (forwardingTarget) {
    return objc_msgSend(forwardingTarget, sel, ...);
  }

  NSMethodSignature *methodSignature = [receiver methodSignatureForSelector:sel];
  if (methodSignature) {
    NSInvocation *invocation = [NSInvocation _invocationWithMethodSignature:methodSignature ...];
    [receiver forwardInvocation:invocation];

    void *returnValue = NULL;
    [invocation getReturnValue:&value];
    return returnValue;
  }
  ...
}

Does Not Recognize Selector, the Gauche Goodbye

The slow path, forwardInvocation:, is dependent on the ability to return a method signature for the given selector. If nil is returned then all hopes are lost and runtime calls doesNotRecognizeSelector: and then terminates the process, literally calling kill(getpid(), 9).

In doesNotRecognizerSelector: you can do whatever you want, but realize that it is the final stop. Unfortunately all you have is the selector and it's hard to do much with it alone. If you wanted any more information then you should have returned a method signature, silly!

Usually you won't implement doesNotRecognizeSelector: and so you'll be getting NSObject's implementation which just logs an error message that you definitely seen before

-[__NSCFNumber objectAtIndex:]: unrecognized selector sent to instance 0x8a48b70

and then it throws an NSInvalidArgumentException that you can catch if you so like. Be aware that if for any reason you override doesNotRespondToSelector: yourself then you *must* throw for the chance of being caught, lest you suffer the imminent process-kill just around the corner.

void __forwarding__(BOOL isStret, id receiver, SEL sel, ...) {
  id forwardingTarget = [receiver forwardingTargetForSelector:sel];
  if (forwardingTarget) {
    return objc_msgSend(forwardingTarget, sel, ...);
  }

  NSMethodSignature *methodSignature = [receiver methodSignatureForSelector:sel];
  if (methodSignature) {
    NSInvocation *invocation = [NSInvocation _invocationWithMethodSignature:methodSignature ...];
    [receiver forwardInvocation:invocation];

    void *returnValue = NULL;
    [invocation getReturnValue:&value];
    return returnValue;
  }

  // doesNotRecognizeSelector: should throw to avoid the SIGKILL.
  [receiver doesNotRecognizeSelector:sel];
  kill(getpid(), 9);
}

The basic skeleton of the forwarding path has emerged. This still has some details missing but read on the know more!

Proxy Objects

The forwarding path has two routes: a fast one that simply allows a different target and a slow one that allows you to do anything at all with the invocation. These two combine to give the power to implement proxies in Objective-C.

proxy is an object that provides an interface to something. That might sound like the most vague definition that you have ever heard, and it is. But one way to to think about them is that they bridge the interface from one object to another.

Let's look at some examples.

Wrapper/Forwarding Proxies

Here's an incredibly contrived example of a class that wraps a scroll-view delegate. It logs in scrollViewDidScroll: and forwards all methods to the wrapped delegate. This could be implemented by overriding every scroll-view delegate method but that is not future-safe and a pain to maintain. This example is simple but should illustrate the power of a forwarding target.

@interface ScrollLoggingScrollViewDelegate : NSObject <UIScrollViewDelegate>
- (instancetype)initWithScrollViewinnerDelegate:(id<UIScrollViewDelegate>)innerDelegate;
@end

@implementation ScrollLoggingScrollViewDelegate
- (instancetype)initWithScrollViewinnerDelegate:(id<UIScrollViewDelegate>)innerDelegate {...}

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
  NSLog(@"%@ is scrolling!", scrollView);
  if ([_innerDelegate respondsToSelector:@selector(scrollViewDidScroll:)]) {
    [_innerDelegate scrollViewDidScroll:scrollView];
  }
}

- (id)forwardingTargetForSelector:(SEL)aSelector
{
  if ([_innerDelegate respondsToSelector:aSelector]) {
    return _innerDelegate;
  }
  return nil;
}
@end

By the very nature of forwardingTargetForSelector: returning an alternate receiver all proxies built using it will wrap one or many objects. To unlock the full potential of proxies one must enter the land of forwarded invocations.

Full-Fledged Proxies

With invocation forwarding it is possible to implementer a multiplexer, which is an object that forwards methods to a set of objects. Here is an example of a class that will forward any method it receives to every object in the collection it was init'd with.

@interface FastEnumerationMultiplexer : NSObject
- (instancetype)initWithEnumerator:(id<NSFastEnumeration>)enumerator;
@end

@implementation FastEnumerationMultiplexer
- (instancetype)initWithEnumerator:(id<NSFastEnumeration>)enumerator {...}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
  for (id object in _enumerator) {
    // We have to use fast enumeration to get the first one.
    // There is no "anyObject" or "objectAtIndex" in NSFastEnumeration.
    return [object methodSignatureForSelector:aSelector];
  }
  return nil;
}

- (void)forwardInvocation:(NSInvocation *)anInvocation
{
  for (id object in _enumerator) {
    [anInvocation invokeWithTarget:object];
  }
}
@end

Then some real magic occurs when a category is added to any class supporting fast enumeration to return an instance of the multiplexer.

@interface NSArray (EnumerationMultiplexing)
- (id)do;
@end

@implementation NSArray (EnumerationMultiplexing)
- (id)do
{
  return [[FastEnumerationMultiplexer alloc] initWithEnumerator:self];
}
@end

This makes it easy to send a method to every object in the collection.

NSArray *array = @[ foo, bar, baz ];
[[array do] dumpCaches];

In just one line we managed to tell all of the objects in the array to dump their caches. Notice that this might get wonky if any object in the array doesn't implement the selector (it'll enter the forwarding path!) or if the selector has a return type (the last one's return type will be the winner) but there are details left to you to make this more robust.

Returning an object that consumes selectors/invocations is an instance higher order messaging, since here it is as if one selector is taking another as an argument. This use of proxies is also explained by Mike Ash in his post on forwarding and is used to implement mocks in a number of test frameworks including OCMock and OCMockito. In the case of mocks forwardInvocation: is used to catch and record all invocations so that expected calls can be verified and unexpected calls can issue an error.

There are many other uses of proxies and you should feel free to explore them and have fun. Adventure is out there!

NSProxy

NSProxy, is one of the few root classes available in iOS and Mac OS X, unless you make your own. It is very similar to NSObject, and even conforms to the NSObject protocol, except that it has less methods, a lot less methods. It even doesn't have an init method! If you subclass it and do create an init method then you don't have to call super! #mindblown

The reduction of methods simplifies the interface and leaves the minimal set needed to proxy the invocations it receives. Yet strangely enough, NSProxy does *not* have forwardingTargetForSelector: but only forwardInvocation: (and obviously methodSignatureForSelector: too). The idea is that forwarding targets are typically found on objects that forward some methods but have a life of their own as well.

Thus if you have some non-proxy methods you are something real, an NSObject. If you semantically an interface with some business logic then you can be an NSProxy and all forwarding takes place in the slow, more robust forwarding path. 

Are Proxies Safe?

After poking around in the bowels of objc_msgSend we've uncovered the forwarding path and from it seen how to build proxies which are quite natural constructions and allow implementing some pretty awesome patterns. There is one thing to beware though, they can be used in a way that is completely unsafe. But you'll be fine as long as you follow one rule:

Never use a proxy to masquerade as an instance of a specific class.

This says that, for example, that you should never have a proxy that claims to be an NSArray when it is actually an NSProxy or some other subclass of NSObject. This means that proxy objects should only be stored as an id type, often qualified with protocols, and as a particular class pointer.

This rule does not necessarily mean to never do it but that if you do, your proxies might be used unsafely. Here's an example proxy that can pretend to be anything.

@interface FakeObject : NSObject
@property (nonatomic, strong) id object;
@end

@implementation FakeObject
- (id)forwardingTargetForSelector:(SEL)aSelector
{
  return _object;
}
@end

And here an example of it being used to masquerade as a UIView.

UIView *proxiedView = [[UIView alloc] initWithFrame:CGRectMake(1.0, 4.0, 9.0, 16.0)];
FakeObject *proxy = [FakeObject new];
proxy.object = proxiedView;
UIView *view = (UIView *)proxy;

NSLog(@"%@", NSStringFromCGRect(view.frame));
NSLog(@"%@", view.layer);

This actually works exactly as you'd expect. Even though this is unsafe it work a lot of the time. The issue is that you never know when it won't...

UIView *parentView = [UIView new];
[parentView addSubview:view];

Sample[98633:70b] -[UIView superlayer]: unrecognized selector sent to instance 0x9073640 Sample[98633:70b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UIView superlayer]: unrecognized selector sent to instance 0x9073640' *** First throw call stack: ( 0 CoreFoundation 0x017e57e4 __exceptionPreprocess + 180 1 libobjc.A.dylib 0x015658e5 objc_exception_throw + 44 2 CoreFoundation 0x01882843 -[NSObject(NSObject) doesNotRecognizeSelector:] + 275 3 CoreFoundation 0x017d5b0b ___forwarding___ + 1019 4 CoreFoundation 0x017d56ee _CF_forwarding_prep_0 + 14 5 UIKit 0x002987ea -[UIView(Internal) _addSubview:positioned:relativeTo:] + 1097 6 UIKit 0x0028bada -[UIView(Hierarchy) addSubview:] + 56 7 Zoof 0x00002c3f main + 639 8 libdyld.dylib 0x01dca70d start + 1 ) libc++abi.dylib: terminating with uncaught exception of type NSException

Adding the view as a subview is apparently not safe. Darn.

What's happening is that addSubview: ends up calling _addSubview:position:relativeTo: in which an ivar is accessed directly off the first argument. That is what causes the kaboom. Let's look at another example to see the issue in more detail.

Ivars and Proxies Do Not Mix

@interface Stringish : NSObject
@property (nonatomic, copy) NSString *string;
@end

@implementation Stringish
- (BOOL)isEqual:(Stringish *)object
{
  return [_string isEqualToString:object->_string];
}

- (NSInteger)hash
{
  return [_string hash];
}
@end

Passing a proxy in for the object parameter in the isEqual: method will cause the program to crash because the implementations reads an ivar right off of it. You can't forward/proxy ivar-lookup. It will try to poke right into the object's innards as if it were what it claimed to be.
Here's how the isEqual: method is going to be compiled. 

- (BOOL)isEqual:(Stringish *)object
{
  Ivar ivar = class_getInstanceVariable([Stringish class], "_string");
  ptrdiff_t offset = ivar_getOffset(ivar);

  NSString *selfString = *(id __strong *)((__bridge void *)self + offset);
  NSString *objectString = *(id __strong *)((__bridge void *)object + offset);

  return [selfString isEqualToString:objectString];
}

If object is actually a proxy then it will obviously why it crashes. There simply is no ivar at that location or if there is, it will be some ivar of the proxy object itself and still be wrong.
The previous example suffered the same fate. The addSubview: call ends up trying to pull out the layer instance variable but it reaches into the proxy and finds the proxied view instead. Total bummer.

Runtime Functions and Proxies Do Not Mix

The proxying and forwarding all discussed so far occurs through objc_msgSend, the invocation of selectors. Any functions that try to tap into the object directly will not work! You can implement class on the proxy to "pretend" that it is some other class but you won't find a way to fool object_getClass.

Similarly if you use class_getMethodImplementation to get an IMP for an object of class Foo and then call the IMP directly on a proxy-to-Foo you are going to have a bad time.

Proxy Interfaces not Implementations

The lessons and the rule above paint a very clear picture. Don't proxy over specific implementations, classes, but instead proxy only over protocols. If you do then you will always be safe. This is similar to "program to an interface not an implementation" in the exact way that doing the latter inadvertently couples the code to unexpected and uncontrollable implementation details.

The one place this rule is easily broken is in tests where you can handle the lack of safety and account for it the few times that it rears its ugly head. However this is something you definitely don't want to risk in product code or the application with die a bitter death when it hits rare/untested code-paths or Apple releases a new OS X that changes an implementation. The latter is unavoidable; don't risk it.

An Implicit Protocol For Everyone

After looking at the forwarding path we find that Core Foundation injects a few subtle assumption into the world. All classes must implement some subset of the forwarding path or madness ensues (the exact madness is revealed in the final showing of the forwarding path). We have seen that forwarding explicitly deals with four methods.

  • forwardingTargetForSelector:
  • methodSignatureForSelector:
  • forwardInvocation:
  • doesNotRecognizerSelector:

It also can indirectly call resolveClassMethod: and resolveInstanceMethod: but those aren't quite the same.

At this point in history, Objective-C and Cocoa have become more and more intertwined over the years (through NSObject, ARC, forwarding, and a few others). It is curious to wonder which of those methods, if any, are essentially required to be implemented by every object that enters the runtime.

forwardingTargetForSelector: is clearly optional. forwardInvocation: doesn't get called if a nil method signature, but then doesNotRecognizeSelector: does.

The execution paths and branch structure of the forwarding path insists that every class is expected to implemented at least methodSignatureForSelector: or the runtime will bark loudly the moment that an unrecognized selector is invoked. The error message logged when this critical method is missing is impossible to misinterpret.

*** NSForwarding: warning: object 0xadd9e58 of class XYZ does not implement methodSignatureForSelector: -- did you forget to declare the superclass of 'XYZ'?

Thus there is a secret and implicit prototol that every object conforms to.

@protocol UnrecognizedSelectorSafelyReceiving
@required
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;

@optional
- (void)doesNotRecognizeSelector:(SEL)aSelector;

// Super optional. Only needed if methodSignatureForSelector: ever returns non-nil.
- (void)forwardInvocation:(NSInvocation *)aSelector;
@end

Although of course no such protocol exists. Hehe.

Now, before the grand finale, the forwarding path in full, we need to look at one more crazy-message-receiving monster, Dr. NSZombie.

Zombies

Choo __unsafe_unretained *aChoo = nil;
@autoreleasepool {
  aChoo = [Choo new];
}

NSLog(@"%@", [aChoo blessYou]);

That's most certainly a dangling pointer (read the post on ARC if it isn't clear why). The object it has pointed to has died and the pointer is left roaming the world as an undead pointer, called a Zombie in Objective-C. The autoreleasepool is used to make sure that the cleanup of the object has occurred and that we are in possession of a real, bona fide zombie.

NSZombie

Running that code snippet will simply crash but if you enable NSZombies then it will give a nice error message before crashing.

*** -[aChoo blessYou]: message sent to deallocated instance 0xadd9e58

The idea is that objects are never actually deallocated when zombies are enabled. Their teardown still released all instance variables and any retained associated objects as well as calling the deconstructors on C++ instance variables. But when zombies are enabled, the final step, a free of the malloc'd memory inhabited by the instance, is skipped.

So then how are Zombies implemented?

The Implementation Requirements of NSZombie

To have the behavior of Zombies seen above there are three requirements.

The first is actually implemented the cleanup behavior described in the previous paragraph. This is implemented in the method _dealloc_zombie, which is a category on NSObject (defined in CF!) that is swizzled in when Zombies are enabled, by calling objc_destructInstance which is identical to object_dispose expect that it skips the free.

The second requirement is that the object have its class changed to NSZombie which is also done in _dealloc_zombie by calling object_setClass.

The final requirement is that the object log an error and then stop the app. The stopping part is simple. It can throw an exception, abort, exit, kill, etc. On x86 it executes the INT3 instruction directly to stop the app in the debugger (smart!).

Storage of the Old Class in NSZombie

There are actually two subtle issues that need to be addressed. First, once it changes the class of the object, it has no way to get it back when it logs the error. It can't actually store the old name directly in the instance because it is very possible that the instance had no instance variables and thus there is no room to do so!

One possible solution would be to use a global table of all zombies. This would quickly bloat to contain every object that has ever lived and died and is just not sustainable, even when debugging.

Instead, the implementation uses a public method in the Objective-C runtime that it advises no one ever actually call directly, lol, objc_duplicateClass. This is roughly equivalent to calling objc_allocateClassPair and then objc_registerClassPair right after it except that it makes tons of optimizations since it knows that you won't be adding method, ivars, properties, or protocols to the class.

This means that it creates a subclass at runtime for the class of every instance that becomes a zombie, of course with a giant cache of all the _NSZombie_ subclasses. And what does it use for the name of the class? A concatenation of the _NSZombie_ and the name of the class that the object was. It stores the old class name IN the new class name. 

If you dump all classes at runtime with objc_copyClassList in the example above, you'll definitely see _NSZombine_Foo. You can also see it by calling object_getClass on the ZombieThat's some nifty, clever stuff.

The Methods of NSZombie

The second subtlety comes when considering the methods that _NSZombie_ has. The first question has to be what the superclass is, since that will determine a set of the methods. It derives from neither NSObject nor NSProxy; if it did then it would have to implement all the methods from its superclass and in each log and halt. This is not ideal and will break with any additions of methods to the superclass (including categories!). Clearly then the only possible solution is that _NSZombie_ is a root class, meaning that it has no superclass.

Given the explanation above about the "secret" protocol, all it really needs to do is implement methodSignatureForSelector: and then log and throw. While this is arguably possible, it is semantically and pedantically wrong for a number of reasons, in decreasing order of danger:

  1. Is it neither correct nor safe to call any method on a Zombie ever. Imagine a future compiler change in ARC where self is retained across a method call. The program would die in objc_storeStrong.
  2. Such an implementation relies completely on the knowledge that methodSignatureForSelector: is all that is necessary for this to work. Using this detail is fragile and makes the pedant in me writhe.
  3. On failure the stack trace would reside arbitrarily inside of methodSignatureForSelector:. Leaking such an implementation detail would be gross, unnecessary, and easily confusing.

This leaves us with only one possible conclusion. Core Foundations forwarding path explicitly checks for the presence of a Zombie. It can't call isKindOfClass: because of problem number (1) above. It could call object_getClass and then call isSubclassOfClass: on that, but once it finds that it is a Zombie it will need to get the class name to extract out the old class name anyway so it might as well just get the name and check for the "_NSZombie_" prefix. Let's attempt to see if that is the case by fooling it.

Compiled with MRR (-fno-objc-arc)

NS_ROOT_CLASS
@interface _NSZombie_Apocalypse
@end

@implementation _NSZombie_Apocalypse
@end

int main(int argc, char *argv[])
{
  Class klass = object_getClass("_NSZombie_Apocalypse");
  _NSZombie_Apocalypse *zombieImpersonator = class_createInstance(klass, 0);
  NSLog(@"%@", zombieImpersonator);

  return 0;
}

So here a cute little program meant to fool the runtime. It creates a class whose name starts with "_NSZombie_". The class must be declared as a root class, otherwise the computer will wag its finger. Then we create an instance of the class; notice that we can't use newallocinit, or even class because we haven't written any of those methods. Hehe. We also have to compile it with MRR because class_createInstance is not available in ARC.

If we run this code with the class name as Not_NSZombie_Apocalypse then the output is

*** NSForwarding: warning: object 0xaddres58 of class 'Not_NSZombie_Apocalypse' does not implement methodSignatureForSelector: -- did you forget to declare the superclass of 'Not_NSZombie_Apocalypse '?
*** NSForwarding: warning: object 0xaddres58 of class 'Not_NSZombie_Apocalypse ' does not implement doesNotRecognizeSelector: -- abort

which is exactly what we would expect. But when the class is _NSZombie_Apocalypse then we get

*** -[Apocalypse  respondsToSelector:]: message sent to deallocated instance 0xaddres58

and we having correctly tricked the runtime into thinking that we have a Zombie!

Core Foundation's Forwarding Path in Full (Almost)

After everything we've seen we are finally ready to put together an almost-complete implementation of Core Foundation's forwarding path.

This takes what we had before and adds the preamble, the Zombie-check, if-guards that call class_respondsToSelector, and a number of error messages that the report when methods do not exist or are implemented as expected.

Perhaps the most fun check is that _objc_msgForwardStret was never called from objc_msgSend and likewise that _objc_msgForward was never called from objc_msgSendStret (this is the "structed-ness" check below).

There is also a guard to ensure that the invoked selector is already registered with the runtime. It simply registers the selector and then verifies that it got the same selector back. Its purpose is to protect some loon from passing a C-string in place of a SEL

objc_msgSend(anObject, (SEL)"doThings:", things);

This is not impossible, and the cast is valid, since under the hood a selector is really just a C-string. Though each selector has a single, unique, registered static address and using any lexically equivalent string will simply not work.

The @selector directive tells the compiler to place corresponding string in the data section of the binary and register it as a selector with the runtime. You can register your own strings using sel_registerName and sel_getUid, if you are nuts/adventurous and want to. The distinction between those two methods dies over the years and now their implementations are identical.

Aborting Warnings

The part of the forwarding path that tickles me the most is that the only call to CFLog that passes kCFLogLevelError is the zombie call. All of the rest of the logs statements are emitted at the warning level, even though they log that they intend to abort and then they absolutely do.

When I was a kid a warning was something just a bit softer.

Curtain Falls

That's all folks.

void __forwarding__(BOOL isStret, void *frameStackPointer, ...) {
  id receiver = *(id *)frameStackPointer;
  SEL sel = *(SEL *)(frameStackPointer + 4);

  Class receiverClass = object_getClass(receiver);

  if (class_respondsToSelector(receiverClass, @selector(forwardingTargetForSelector:))) {
    id forwardingTarget = [receiver forwardingTargetForSelector:sel];
    if (forwardingTarget) {
      return objc_msgSend(forwardingTarget, sel, ...);
    }
  }

  const char *className = class_getName(object_getClass(receiver));
  const char *zombiePrefix = "_NSZombie_";
  size_t prefixLen = strlen(zombiePrefix);
  if (strncmp(className, zombiePrefix, prefixLen) == 0) {
    CFLog(kCFLogLevelError,
          @"-[%s %s]: message sent to deallocated instance %p",
          className + prefixLen,
          sel_getName(sel),
          receiver);
    <breakpoint-interrupt>
  }

  if (class_respondsToSelector(receiverClass, @selector(methodSignatureForSelector:))) {
    NSMethodSignature *methodSignature = [receiver methodSignatureForSelector:sel];
    if (methodSignature) {
      BOOL signatureIsStret = [methodSignature _frameDescriptor]->returnArgInfo.flags.isStruct;
      if (signatureIsStret != isStret) {
        CFLog(kCFLogLevelWarning ,
              @"*** NSForwarding: warning: method signature and compiler disagree on struct-return-edness of '%s'.  Signature thinks it does%s return a struct, and compiler thinks it does%s.",
              sel_getName(sel),
              signatureIsStret ? "" : not,
              isStret ? "" : not);
      }
      if (class_respondsToSelector(receiverClass, @selector(forwardInvocation:))) {
        NSInvocation *invocation = [NSInvocation _invocationWithMethodSignature:methodSignature
                                                                          frame:frameStackPointer];
        [receiver forwardInvocation:invocation];

        void *returnValue = NULL;
        [invocation getReturnValue:&value];
        return returnValue;
      } else {
        CFLog(kCFLogLevelWarning ,
              @"*** NSForwarding: warning: object %p of class '%s' does not implement forwardInvocation: -- dropping message",
              receiver,
              className);
        return 0;
      }
    }
  }

  const char *selName = sel_getName(sel);
  SEL *registeredSel = sel_getUid(selName);

  if (sel != registeredSel) {
    CFLog(kCFLogLevelWarning ,
          @"*** NSForwarding: warning: selector (%p) for message '%s' does not match selector known to Objective C runtime (%p)-- abort",
          sel,
          selName,
          registeredSel);
  } else if (class_respondsToSelector(receiverClass, @selector(doesNotRecognizeSelector:))) {
    [receiver doesNotRecognizeSelector:sel];
  } else {
    CFLog(kCFLogLevelWarning ,
          @"*** NSForwarding: warning: object %p of class '%s' does not implement doesNotRecognizeSelector: -- abort",
          receiver,
          className);
  }

  // The point of no return.
  kill(getpid(), 9);
}

Postscript: The forwarding Path Isn't Open Source

It's a sad fact but it's true. It lives in the private part of Core Foundation. Sad panda!

A lot of the code in this post was put together with patience knee-deep in disassembly and poking around in Xcode + LLDB. Teasing out all the details was a great adventure for me and I hope it has been for you too.