Abusing Objective C With Class

April 12th, 2007

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;

22 Responses to “Abusing Objective C With Class”

  1. Jonathan Wight Says:

    That’s actually really nice.

    When I’ve been in like circumstances I’ve used NSInvocations (and maybe my invocation grabber classes to make it more convenient).

    I might have to switch techiques.

  2. Elliott Harris Says:

    Good stuff.

    Your examples left me thirsty for a cup of coffee, AND taught me something neat about objc_msgSend.

  3. Jan Van Boghout Says:

    Why make it so complicated? You can just declare an interface category on NSObject. That gets rid of any compiler warnings, makes sure all primitive types are handled well, and serves as an informal protocol at the same time.

    @interface NSObject (CoffeeDelegate)
    – (BOOL) coffeeMaker:(RSCoffeeMaker*)theMaker shouldBrewCoffeeForScheduledDate:(NSDate*)scheduledDate;
    @end

  4. Jonathan Wight Says:

    Jan, you missed the point a little.

    Daniel’s example doesn’t show the right problem to the solution he presents. Imagine that the selectot is dynamic (perhaps stored in a container object). In that case you may not be able to just create a category.

  5. Daniel Jalkut Says:

    I definitely could have used a less contrived example. I’ve added an update to the post which hopefully adds a little context.

  6. Jonathan Wight Says:

    Also, for the record, using NSInvocations is at least 6 statements (+1/parameter), assuming you’re not using InvocationGrabber or something similar . This technique could save a lot of code

  7. Jan Van Boghout Says:

    Ahh, I was wondering that as I posted. With purely dynamic selectors, I agree that this is a cool technique.

  8. leeg Says:

    This is covered (vaguely) in the comp.lang.objective-c alternative FAQ. It’s interesting to note that the GNU objc runtime has a single function for messages with any return type, so you don’t trip up if instead of a BOOL, you’re expecting a floating-point or struct return.

  9. Jonathan Johnson Says:

    While I agree with your sentiments, the statement: “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.” is actually correct, but for the wrong reasons. On PowerPC, if the return type is 4 bytes or less, the value is placed in r3. On Intel, the same is true, but with the EAX register. In each case, your chances of failure are equal on both platforms if you took these at face value. However, there is a different reason it fails on Intel but not PowerPC — on PowerPC BOOL is actually four bytes, which matches the size of id. On Intel, BOOL is only 1 byte, and hence the failures because of truncation instead of overflowing.

    Anyways, like I said your points are valid, but I always like knowing the underlying causes, that way when I can abuse something, I know that it will work ;)

  10. Lucas Eckels Says:

    It’s true that using NSInvocation might be somewhat more code, it is the documented solution to this problem — it’s right on the documentation for [NSObject performSelector:withObject:withObject]. This function pointer approach just moves the possibly-problematic behavior to a different place. Now if Apple decides to tweak what runtime functions get called for what return values, you’ll have broken low-level code all over your project.

    Let the Apple worry about doing the right thing in NSInvocation. If you’re worried about the amount of code, just right a category on NSObject with -(BOOL)performBoolSelector:withObject:withObject: that does all the NSInvocation jazz.

  11. patrick Says:

    While this technique is certainly ‘clever’, it may not be the best solution. There are a number of reasons why I would probably prefer NSInvocation:

    1) The documentation of performSelector: endorses it: “For methods that return anything other than an object, use NSInvocation.”

    2) The Objective-C Programming Language has this to say about objc_msgSend(): “The compiler generates calls to [objc_msgSend]. You should never call it directly in the code you write”.

    Sounds like they mean it ;-)

    3) Looking at objc-runtime.h it seems that you might need a different flavor of objc_msgSend depending on the return type or the computer architecture. Real world code could therefore be more complicated than the idealized example given here.

  12. Daniel Jalkut Says:

    The fact that one set of documentation says not to use objc_msgSend is offset quite a bit by the fact that another document (the Objective C Runtime Reference) explains in detail the proper uses of each of the functions made publicly available by the header files in /usr/include.

    In other words, as far as “hacks” go, this falls pretty low on the risk chart.

    It’s probably true that NSInvocation is a more stylistically consistent solution for Objective-C code, if you can afford to use it. But I consider it more useful for situations where capturing all the logic of a message invocation in a single transferrable object is important.

    Consider that objc_msgSend is a compile-time implementation detail. This means that your direct invocation of the function, if done in accordance with the conventions of the compiler, are indistinguishable from “legitimate” code generated by Objective-C syntactic messages. So your code will continue to work as long as other compiled code of this era continues to work.

    Know what you’re getting into, but use objc_msgSend with confidence when necessary or optimal.

  13. Eric Albert Says:

    A couple points here….

    First, as with most differences between PowerPC and Intel Macs, this is covered in the Universal Binary Programming Guidelines (and has been since the Intel announcement). See Objective-C Runtime: Sending Messages, which happens to include this specific example. I helped write this section and made sure it got included in the document, so I take a little bit of pride in it. :)

    Second, Jonathan Johnson’s comment about why this behaves differently on the two architectures is close but not quite right. The C99 ‘bool’ type is a different size on PowerPC and Intel — four bytes on PowerPC and one byte on Intel — but the Objective-C BOOL type is the same size on both. You can see this in objc.h, where BOOL is typedef’d to a signed char.

    Why, then, does this sometimes behave differently on PowerPC and Intel? On PowerPC, the fastest way for the callee to write a one-byte value to the return register (r3) is to fill the entire register, so the compiler always ends up setting the four bytes in r3 to 0x0 or 0x1. If the caller thinks all four bytes were written, the result is the same. On Intel, the fastest way for the callee to write a one-byte value to EAX is sometimes to write the full register, as on PowerPC. Those cases behave the same they do on PowerPC. But sometimes the compiler decides to use one of the instructions which writes to AX (the low two bytes) or AL (the low byte) instead. From the compiler’s perspective that’s legal because it’s writing out the return value for a function which is declared to return a single byte…but a caller which think it’s getting four bytes back and reads those four bytes will end up seeing garbage in the high two or three bytes. That’s when you realize you need a function pointer, since the function pointer tells the compiler that it’s calling a function which is only guaranteed to set one byte in the return value.

  14. Viuto Says:

    … and sometimes you may also need to use gcc’s -mstackrealign flag to avoid Intel x86 issues …

    http://0xced.blogspot.com/2006/06/using-gnu-lightning-on-intel-mac.html

  15. Todd Ransom Says:

    I would avoid using this solution if at all possible because it is not obvious what that line of code does at a glance. A year or three from now when you or a colleague go back to look at this method, will you know the purpose of that line? Sure, you can add comments to it, but I would much rather create a category and implement -(BOOL)performBoolSelector:withObject:withObject: as Lucas suggests, because it looks and feels like Cocoa code instead of some gcc appeasement voodoo.

    .02,
    Todd

  16. Daniel Jalkut Says:

    Eric: As usual, I suffer from “in one ear, out the other” syndrome until the day I actually need it :) The documentation you refer to is both good and concise. Nice work!

    Todd: the category method is probably a good idea, or at least an instance method encapsulating the “ask delegate for answer.” But in any case the example I pasted here is both contrived and poorly documented – only intended to be a bare summary of how you might achieve a goal, given a requirement.

  17. cjwl Says:

    I must have been sleeping when performSelector: broke for this situation, thanks for the post. I would avoid objc_msgSend though and to use your example:

    [Ed. Note: Code sample cut for formatting and brevity:]

    BOOL (*MyMagicSender)(id, SEL, id, id)=(BOOL (*)(id, SEL, id, id))[mDelegate methodForSelector:delSelector];

    if(MyMagicSender!=NULL){
    shouldBrew = MyMagicSender(mDelegate, delSelector, self, [brewTimer fireDate]);
    }

    If I wanted forwarding to work I’d go with NSInvocation’s.

  18. Jürgen Schweizer Says:

    Daniel, please correct me if I am wrong, but your code does not seem to check, whether the delegate actually returns a BOOL. As an alternative, I suggest to let the delegate return an NSNumber. This way you will be able to dynamically check (-[NSObject isKindOfClass:]) whether you actually get an object of the correct class.

  19. Daniel Jalkut Says:

    Jürgen – you’re not wrong – my code doesn’t check. But I think this is a kind of check that can be implemented at the discretion of the coder. Especially if the delegate sender and receiver are both written by the same coder, who is highly unlikely to ever flagrantly return a non-BOOL from a BOOL-returning delegate method :)

    Checking the type of the object seems like overkill to me in many cases.

  20. Jürgen Schweizer Says:

    I agree with the overkill assessment in the case when all of the code is written by the same author. But I was actually thinking of a framework developer. I believe the dynamic approach you outlined above makes the most sense in frameworks where the actual application is not known at the time of development. And if you go with an informal protocol there is always a slight chance of misuse :).

  21. Shawn Erickson Says:

    Quick example that uses NSInvocation that supports an arbitrary selector to be used… DynamicSelectorExample.m

    Note many optimizations could be made (cache the invocation object, etc.).

  22. Simon Says:

    I’ve spent far more hours than I want to think about debugging stack corruption resulting from incorrect casts of function pointers and pointers-to-members.

    I’m currently porting Win32/MFC code (in my Windows-bound day job) from Visual C++ 6.0 to Visual Studio 2005. This problem is particularly insidious with MFC’s message map macros of old which are full of yummy (and decidedly dodgy) casting.

    Making assumptions about function prototypes, calling conventions, return types at the C level is always a bad idea.

    Apple provides a solution with NSInvocation and it even lets you check the size of the return type against your expectations, so I’d prefer to go with that, even if I have to type a few more lines to buy that extra safety…

Comments are Closed.

Follow the Conversation

Stay up-to-date by subscribing to the Comments RSS Feed for this entry.