I don’t code much in Objective-C these days1 but one thing I miss about it compared to Swift is how nil
works.
To be fair to Swift, the concept of “nothing” in Objective-C is kind of a mess, since there’s four different versions of it compared to two in Swift.
nil
| An empty Objective-C object. |
Nil |
An empty Objective-C class. |
NULL |
Nothing in C. |
NSNull |
Mostly used to represent null in Objective-C collections. |
The reason NSNull
exists is that Objective-C collections (NSArray
, NSDictionary
) use nil
in other ways. NSArray
is nil
terminated, so if you did this:
NSArray *array = @[@"a", @"b", nil, @"c"];
// array = [@"a", @"b"]
You'd only have the first two elements available. In NSDictionary
, setting a key to nil
removes it from the dictionary, so NSNull
is used to say “this exists in the dictionary, and it is nothing.”
The main thing to remember is that if you have a pointer assigned to NSNull
or NULL
and you try to do something with them, your app will crash. nil
on the other hand is kind of magic. nil
evaluates to NO
(false
), and nil
as an object always returns nil
. That means that because whatever you sent to nil
will return nil
and that will evaluate to false instead of crashing your app that this:
if (dict != nil && dict.containsObjectForKey[@"a"]) { ... }
Can become this:
if (dict.containsObjectForKey[@"a"]) { ... }
This is probably the least-Swift thing ever, but, if you're used to coding in this style, it's fantastic. In a lot (most?) cases where there's a possibility that dict
could be nil
, the thing you want is for nothing to happen. Way back when Swift was first released, and Brent said switching to Swift “feels like a Jedi who’s trying to switch from a lightsaber to a blaster,” this is what I thought of.
- Although a lot more than none, and I still think it’s important to know if you’re writing software for Apple Platforms. ↩
@implementation NSNull(GJLNilReceiver)
– (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
return [super methodSignatureForSelector:aSelector] ?:
[NSMethodSignature signatureWithObjCTypes:”@@:”];
}
– (void)forwardInvocation:(NSInvocation *)anInvocation
{
[anInvocation invokeWithTarget:nil];
}
@end
That is a neat way to get around `-[NSNull null]` causing an exception.
Think you have a typo, should ‘recodesent’ be ‘recode sent’?
Thank you! Fixed.
My solution to NSNull issues https://stackoverflow.com/a/16610117/1633251
I have not written objc code for about 25 years or so, so take this with a grain of salt. If I remember correctly this nil behavior caused me a lot of headaches, since execution of a nil object is effectively a noop.
It’s a very fast way to get your app to behave in a weird way (some logic does not execute) and it does so SILENTLY.
Frankly, I think it’s a terrible choice and the whole null problem above is more of a conundrum of bad decisions which have accumulated over time
I like what Common Lisp does. There’s a constant NIL, which evaluates to NIL, and is also used for false and terminating lists. It’s easily handled by most everything implicitly, except for such things as modification. The closest concept to NULL Common Lisp has is the concept of bound and unbound for symbols and slots in its object system, but these cause recoverable errors to be signalled. The examples of NSArray and NSDictionary are amusing to me. Common Lisp arrays can have what’s called a FILL-POINTER to hide elements. As for hash tables, there are the functions GETHASH and REMHASH, rather than setting a key to remove it of all things; what happens when I want to explicitly store NIL; the former function returns a second value, useful for this case, indicating whether the value were explicitly present or not. So, a GETHASH for a key not present returns NIL NIL, and a GETHASH for a present key holding NIL returns NIL T; the second value can be implicitly ignored for cases in which it be unimportant.
Ada is another language which does this well. Its modelling abilities are better than most languages. Suffice to write, it’s very easy to know every possible value for something. The only at which place NULL comes into play is the access type, which is also very easy to avoid for many programs, due to Ada’s design; even then, an access type can be specified to never have NULL as a possible value, and an error would be thrown if it were ever assigned such. Detailed modelling is necessary for good software.
This article unfortunately doesn’t seem entirely accurate.
nil == Nil == NULL == 0
They’re only superficially different but behave exactly the same, and you can use one anywhere you use the other. For example, you can send an Objective-C message to NULL.
Of course if you dereference a NULL pointer, it will crash, but the same is true of a nil pointer.
NSNull is a class. You can send Objective-C messages to NSNull, most relevantly, [NSNull null] to return the singleton instance of NSNull. And you can send Objective-C messages to the singleton instance of NSNull too, isEqual: or isKindOfClass: for example. This doesn’t crash, unless you send them a message they don’t respond to.
The return value of a message to nil isn’t always guaranteed to be 0 in all circumstances on all architectures. See for example this old blog post:
https://ridiculousfish.com/blog/posts/nil.html
Correct. I definitely mixed up/misrepresented the nil/NULL thing. I’ll post a follow up for that. I probably should have gone into more detail on the NSNull thing. I knew all of that but feel like I described it differently. I’ll make sure to mention that.
And thanks for the correction 🙂