What’s wrong with this macro?

What is wrong with this Objective-C (and C/C++) macro?

#define RETAIN_PROPERTY(propertyName, newValue) \
    do { \
        if (propertyName##_ != newValue) \
        { \
            [newValue retain]; \
            [propertyName##_ release]; \
            propertyName##_ = newValue; \
        } \
    } while(0)

Looks pretty benign right? Well it is when you call it like this:

id n = [NSNumber numberWithInt:10];

But what happens when you do this:

RETAIN_PROPERTY(propName, [NSNumber numberWIthInt:10]);

Well macros are just that, macros. They generate code. They substitute the text of the macro parameters and emit the body into your source. Unlike methods or functions, they do not evaluate their parameters before passing the result to the macro. So the macro above when called the second way will produce the following code:

    do {
        if (propName_ != [NSNumber numberWIthInt:10])
            [[NSNumber numberWIthInt:10] retain];
            [propName_ release];
            propName_ = [NSNumber numberWIthInt:10];
    } while(0);

Not quite what we wanted right. We’re leaking an NSNumber instance and creating two which are auto-released and we’ll probably have a zombie object stored in propName_ after the auto-release pool is drained. A nice confusing bug for us to stumble upon.

So how do we fix this?

Like so:

#define RETAIN_PROPERTY(propertyName, newValue) \
    do { \
        __typeof__(newValue) __A = (newValue); \
        if (propertyName##_ != __A) \
        { \
            [__A retain]; \
            [propertyName##_ release]; \
            propertyName##_ = __A; \
        } \
    } while(0)

The line __typeof__(newValue) __A = (newValue); forces the passed parameter to be evaluated and a result stored in the temporary __A. We can then use the temporary variable multiple times within the macro body without fear.

Bug fixed and we can get back to being productive coders.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s