If you send a message to (call a method of) an object that is nil
(NULL
) in Objective-C, nothing happens, and the result of the message is nil
(aka 0
, aka 0.0
, aka NO
aka false
). At least most of the time. There is an important exception if sizeof(return_type) > sizeof(void*)
; then the return-value is undefined under PowerPC/iPhone, and thus all Macs for the next several years. So watch out if you are using a return value that is a struct, double, long double,
or long long
.
In Objective-C, it is valid to send a message to a
nil
object. The Objective-C runtime assumes that the return value of a message sent to anil
object isnil
, as long as the message returns an object or any integer scalar of size less than or equal tosizeof(void*).
On Intel-based Macintosh computers, messages to anil
object always return0.0
for methods whose return type isfloat, double, long double,
orlong long.
Methods whose return value is astruct
, as defined by the Mac OS X ABI Function Call Guide to be returned in registers, will return0.0
for every field in the data structure. Otherstruct
data types will not be filled with zeros. This is also true under Rosetta. On PowerPC Macintosh computers, the behavior is undefined.
I was recently bitten by this exceptional behavior. I was using an NSRange
struct
describing a substring; but the string was nil
, so the substring was garbage. But only on a PPC machine! Even running under Rosetta wouldn’t have reproduced the bug on my MacBook Pro.Undefined values can be a hard bug to detect, because they may be reasonable values when tests are run.
Code running in the iPhone simulator will return all-zero stucts when messaging nil
, but the same code running on an actual device will return undefined structs. Be aware that testing in the simulator isn’t enough to catch these bugs.
A few simple guidelines can help you avoid my misfortune:
- Be especially careful using of any objective-C method that returns a
double, struct,
orlong long
- Don’t write methods that return a
double
,struct
, orlong long
. Return an object instead of astruct
; anNSNumber*
orfloat
instead of adouble
orlong long
. If you must return a dangerous data type, then see if you can avoid it. There really isn’t a good reason to return astruct
, except for efficiency. And when micro-optimizations like that matter, it makes more sense to write that procedure in straight-C, which avoids the overhead of Objective-C message-passing, and solves the undefined-return-value problem. - But if you absolutely must return a dangerous data type, then return it in a parameter.
Bad:
- (double) crazyMath;
Good:
- (void) crazyMathResult:(double*)result;
.
I love Objective-C’s “nil messaging” behavior, even though it is rough around the edges. It’s usefulness is beyond the scope of this article, but it can simplify your code if you don’t return a data-type that is larger then sizeof(void*)
. With time, when the intel-style return behavior can be universally relied on, things will be even better.
It is possible to log every message sent to
nil
, see http://www.friday.com/bbum/2008/01/02/objective-c-logging-messages-to-nil/Comment by Vincent Gable — March 20, 2009 @ 9:35 pm
Yes, a very interesting characteristic of objective-c to take note of… it was one of the first things I learned about when I started to study the language. It was a bit confusing at first but I’ve definitely taken advantage of it in my code.
Comment by Steve — April 27, 2012 @ 8:55 pm
Your link from ‘The Fully Story:’ (by the way, this should be ‘The Full Story:’) no longer works. I did a Google search, though, and found something similar to it at the URL ; you just have to click through the Adobe Reader Table of Contents sidebar to find it under first ‘Guidelines for Special Scenarios,’ then ‘Objective-C: Sending Messages to nil.’
Comment by Bryce Glover — April 4, 2014 @ 10:47 am