One thing that developers who are new to Objective-C will notice immediately are the number of null keywords. We have a choice of:
Under the hood there isn’t much difference between these (as of this writing they are all defined exactly the same way), but by convention they have some subtle variations, and they have some odd behavior around them that it pays to understand.
All three of these values represent null, or zero pointer, values. The difference is that while NULL represents zero for any pointer, nil is specific to objects (e.g., id) and Nil is specific to class pointers. It should be considered a best practice of sorts to use the right null object in the right circumstance for documentation purposes, even though there is nothing stopping someone from mixing and matching as they go along.
A “cute” feature of Objective-C is that it allows messages to be passed to nil objects, simply returning a zero instead of throwing an exception. This removes the problem with NullPointerExceptions in Java–or segmentation faults in C/C++–and allows the developer to avoid peppering their code with null checks. On the other hand, it can cause some problems if the developer is not aware of some of the quirks this behavior can cause.
First, the good news. A common pattern in Java is to do a null check before dereferencing a value, especially if that value comes from a Map or similar object:
Not performing that initial check is a recipe for disaster, and static code analyzers such as FindBugs specifically look for a lack of null testing.
Due to this behavior in Objective-C, we can just test the function directly:
If value is nil then it will semi-silently fail, returning zero.
This is a Good Thing™ in that it helps with the DRY principle and eliminates boilerplate code, and also reduces the probability of the system blowing up dramatically in front of a client (footnote: I cannot count the number of times I’ve seen stack traces for NullPointerExceptions in bug tracking tickets…). On the other hand it can lead to some interesting situations if a nil isn’t tested for and the zero can be used as a legitimate value. For example, assume that num is of type NSDecimalNumber and that it is (unexpectedly) nil:
NSLog, num2 doubleValue ;
Will log something along the lines of:
2009-01-02 23:10:21.809 MyAppName[17399:10b] 0.00
It will not distinguish between an NSDecimalNumber with the value set to zero and a nil object.
This just emphasizes the importance of good unit tests and the saying that untested code is broken code.
The Size of the Returned 0
Something that can cause the program to unceremoniously blow up is that the 0 value returned by calling methods on nil are of a particular size. If the return type is greater than the size of a pointer in the system, then the value will not be cast correctly.
Adding nil Values to Collections: NSNil
One of the common uses of the nil object is as a terminator to a list of objects. For example, if I want to create a NSSet with three objects, named obj1, obj2, and obj3:
mySet setWithObjects obj1, obj2, obj3, ;
This pattern is convenient and allows one to generate lists “on the fly,” but it also means that we must check all objects we are putting into a collection to make sure they are not nil, or we will either see an exception or a truncated list.
It is also sometimes useful to be able to add nil objects as values in NSDictionary objects or to allow for them in lists. In these cases Cocoa provides us with NSNil. NSNil is a language wide constant that is used to represent nil values when we need to store them in collections.