Dynamic messaging is one of the nifty features of Cocoa and Objective-C programming on the Mac. You don’t have to know which class, or even which method your code will call until runtime. The feature is utilized a great deal by the delegation pattern employed by most standard classes in AppKit. For instance, when a window is threatening to close, Apple yields the final decision making to a delegate method, if present:
- (BOOL) windowShouldClose:(id)sender;
If you implement this delegate method and return NO for a particular window, then the user’s attempts to close it are thwarted (hopefully because you just did something friendly like ask them if they were sure or not).
If we’re agreed that delegation and dynamic messaging are a good idea, we should be prepared to use them in our own code as well. Let’s say we’re writing a custom class where delegation of this kind would be useful. We’d like to be as much like Apple as possible, to further solidify the patterns that we all use on a regular basis.
My example application is a coffee maker, and it makes coffee at regular intervals unless the delegate method believes it should not:
- (BOOL) coffeeMaker:(RSCoffeeMaker*)theMaker
shouldBrewForScheduledDate:(NSDate*)scheduledDate;
What must the code in the coffee maker do in order to satisfy this contract? The “scheduledBrewTimerFired:” method might look something like this:
- (void) scheduledBrewTimerFired:(NSTimer*)brewTimer
{
BOOL shouldBrew = YES;
// Give the delegate a chance to reject
SEL delSelector =
@selector(coffeeMaker:shouldBrewForScheduledDate:);
if ((mDelegate != nil) &&
([mDelegate respondsToSelector:delSelector] == YES))
{
shouldBrew = [mDelegate performSelector:delSelector
withObject:self
withObject:[brewTimer fireDate]];
}
if (shouldBrew == YES)
{
//...
}
}
Nifty, right? Yes, nifty. But wrong. What’s the problem? It’s that line where performSelector is called. If you have as many warnings turned on as you should, you’ll see something along the lines of “warning: assignment makes integer from pointer without a cast.” The reason? The performSelector method returns an “id”, not a “BOOL”. We’re expecting magic to happen here, and those of us who grew comfortable with the nuances of PowerPC-based calling conventions have seen magic happen here more often than is healthy.
By violating the function prototype for performSelector, we’re robbing the compiler of its ability to accurately produce assembly code that retrieves the BOOL return type correctly. That means we’ll get the wrong answer some percentage of the time, where that percentage is determined by the degree to which BOOL and id return types are handled differently on the platform.
My first instinct in addressing this problem was to go down a layer. Surely if performSelector is using an unsatisfactory prototype, I could just drop down to objc_msgSend and handle the messaging myself. But alas! The lower-level function also returns an id. At this level, there are some special functions such as objc_msgSend_stret, which messages an object and returns a structure, but there isn’t a handy objc_msgSend_boolret for our convenience.
So what’s a conscientous developer to do? I was lucky to discover a mailing-list thread, in which the answer to this question was beautifully outlined by Greg Parker of Apple. Credit also goes to Rosyna of Unsanity, for asking the question that led to Greg’s answer. Rosyna later expressed appreciation in a follow-up post.
So what’s the trick? In order to get the freedom of dynamic messaging combined with the complete cooperation of the compiler, we need to define a custom function pointer. This points to the same address in memory as the objc_msgSend function, but is defined with a differing prototype. This way we get the message delivery functionality of the Objective-C runtime, while letting the compiler in on the fact that the method being messaged will in fact return a BOOL typed response.
#import <objc/objc-runtime.h>
- (void) scheduledBrewTimerFired:(NSTimer*)brewTimer
{
bool shouldBrew = YES;
// Give the delegate a chance to reject
SEL delSelector =
@selector(coffeeMaker:shouldBrewForScheduledDate:);
if ((mDelegate != nil) &&
([mDelegate respondsToSelector:delSelector] == YES))
{
// Give the compiler a clue - courtesy of Greg Parker
BOOL (*MyMagicSender)(id, SEL, id, id) =
(BOOL (*)(id, SEL, id, id)) objc_msgSend;
shouldBrew = MyMagicSender(mDelegate, delSelector,
self, [brewTimer fireDate]);
}
if (shouldBrew == YES)
{
//...
}
}
Nifty, right? And correct, too. Enjoy!
Update: Paul Kim via chat, and Jan Van Boghout in the comments below each noticed a flaw in my contrived example. Since I already know the selector I’m planning to call, I could just define it explicitly with a regular Objective-C method prototype. Then the compiler would know exactly how to generate the code if I called the delegate directly:
shouldBrew = [mDelegate coffeeMaker:self
shouldBrewForScheduledDate:[brewTimer fireDate]];
That’s true, and it’s a fault in my overly-contrived example. But imagine a more dynamic scenario, where the prototype of the method is known, but not the selector. For instance, if the class offers clients the ability to specify a selector:
- (void) scheduleBrewingAtDate:(NSDate*)brewDate
withBrewDelegate:(id)brewDelegate
withDelegateSelector:(SEL)delSel;