The elegance in messaging nil

hsoi blog, talk 0 Comments

One thing I like about the Objective-C language is that it’s safe to message nil objects. Straight from Apple:

A nil value is the safest way to initialize an object pointer if you don’t have another value to use, because it’s perfectly acceptable in Objective-C to send a message to nil. If you do send a message to nil, obviously nothing happens.

Note: If you expect a return value from a message sent to nil, the return value will be nil for object return types, 0 for numeric types, and NO for BOOL types. Returned structures have all members initialized to zero.

This is a language guarantee.

Take note of the second part: that if you message a nil object and that message provides a return value, you’ll get a returned value of “zero”. The above two statements allow us to make some more elegant choices in how we write our code.

A simple one that often comes up in style guides is checking for nil or non-nil objects in statements. So you might do something like this:

if (anObject == nil) {
    // do something
}

or

if (anObject != nil) {
    // do something
}

And while there’s nothing necessarily wrong with that, consider the alternative:

if (anObject) {
    // do something
}

and

if (!anObject) {
    // do something
}

The two approaches achieve the same end, but the latter puts less on screen. There is less visual clutter to have to read and make sense of. In simple cases like this it may not seem like a big deal, but when you start having compound statements with many nested && and || and parentheses, the reduction in character count goes a long way towards helping code readability and thus maintainability.

Furthermore, consider the “in English” read of the code. It’s “if an object is/isn’t nil” vs. “if we (don’t) have an object”. One isn’t necessarily more correct than the other, but how code winds up being read influences our perception and understanding of the code and can again have powerful effect upon maintainability. I used to be strongly in the former camp but even after all my years of writing code, opted to change to the latter because it is clearer and more maintainable, while being perfectly legal.

Another example is working with returned values.

Let’s say you have a method that returns an NSString. Before proceeding, you need to ensure you have a valid, non-empty string. A common approach might be:

NSString* string = [anObject methodThatReturnsAString];
if ((string != nil) && ([string length] > 0)) {
    // do something
}

We get the string; we ensure we have a valid, non-nil object; then ensure it is non-empty. If those are all true, we proceed. Contrast with this code:

NSString* string = [anObject methodThatReturnsAString];
if ([string length]) {
    // do something
}

This code winds up with the exact same logic check and will only execute the body of the if-statement if the same conditions are met. Again, this is playing off the aforementioned guarantees of the language, in terms of the safety of messaging nil objects and that if you do and the return a value, what that returned value will be. In this case, if string is valid, the call to -length will return the string’s length, and if the length is non-zero it will proceed. One subtle point here is -length returns a NSUInteger, so we know the return value is either 0 or a positive integer; if it returned a NSInteger, perhaps to use a value like -1 to indicate a special case, then you may need to perform the > 0 check.

Now consider if string is nil. If it is, then -length will return 0 because of the language runtime guarantee, and the if-statement evaluates down to an if (0), which is false, and the if-statement body will not be executed. Can you see how this latter code does precisely the same thing as the former code? And the latter code is smaller, more compact, right to the point, easier to read and thus maintain.

There is a trade off. Doing the first can be more efficient because there is no messaging taking place, if string is nil. But is this really a performance problem? Typically no, and I would say it would be a premature optimization to put the nil-check in, at the expense of increased maintainability. However, when you are working with classes/objects where you know there may be some side-effect, or some long computation triggered and thus having the short-circuit in there could wind up with better code? By all means do so, and perhaps add a comment to explain your code choice (as it may be non-obvious to the code reader why you made that code choice, especially if it might be violating a style guide).

Another trade-off is that some people may not understand the latter code. They may be inexperienced, they may not be aware of the nil-messaging aspects of the language and runtime, thus they may think this code is not considering the nil case and could risk a crash. Understandable, since in C and C++, dereferencing NULL is a fine way to crash. To me, this is where style guides are useful, as it can point out and explain this approach. As well, if someone asks you about this approach, if they question your choice or in a code review point out you missed a possible code path, it becomes a teachable moment and you can explain this feature of the runtime. Everyone walks away a little wiser, and that’s a good thing.

Don’t be afraid to message nil objects. It’s a feature of the runtime, and allows for much more elegant and maintainable code.

Leave a Reply