Casting Objective-C Message Sends

Mike Ash shares interesting news that the latest Xcode SDKs include a change to the function prototype of Objective-C’s msgSend family of functions. Where objc_msgSend was previously defined in terms of the couple of parameters it usually takes, and with the return type that it sometimes has, it is now declared as taking no parameters and returning no value:

OBJC_EXPORT void
objc_msgSend(void /* id self, SEL op, ... */ )

In practial terms, this will have an impact if you are still using direct objc_msgSend calls anywhere in your code. For example, imagine you have a “transformer” class that is capable of performing a variety of text manipulations on strings. You might have some code that derives a “SEL” programmatically and then messages the transformer to perform the action. Here’s a contrived example:

SEL tSEL = @selector(uppercaseString:);
NSString* upString = objc_msgSend(transformer, tSEL, lowString);

While that would have worked previously (apart from some ARC warnings), on the latest SDKs you’ll get a compile-time error on the objc_msgSend call:

Too many arguments to function call, expected 0, have 3

Obviously, you need to pass the arguments or the invocation will be useless, but how do you do it? Mike’s post has the advice:

Because it still has a function type, you can still cast it to a function pointer of the appropriate type and invoke it that way. This will work correctly as long as you get the types right.

As long as you get the types right … so, how does one do that? Mike includes an example of inline-casting objc_msgSend, but if you need to do this more than once in your code, I think a more elegant way of casting objc_msgSend is by declaring a global variable as a function pointer with the desired types:

#import "objc/message.h"

NSString* (*PerformWithStringReturningString)(id, SEL, NSString*) = (NSString* (*)(id, SEL, NSString*)) objc_msgSend;

Now when you want to invoke “objc_msgSend” on an object that you know accepts and returns a string type, you can do so like this:

NSString* upString = PerformWithStringReturningString(transformer, tSEL, lowString);

No compiler warnings, ARC knows just what to do with all the types, and you have a very clear understanding of what objc_msgSend is expected to do with this particular invocation.