nil / Nil / NULL / NSNull

来源:互联网 发布:象棋大师软件下载 编辑:程序博客网 时间:2024/04/23 22:33

Understanding the concept of nothingness is as much a philosophical issue as it is a pragmatic one. We are inhabitants of a universe of somethings, yet reason in a logical universe of existential uncertainties. As a physical manifestation of a logical system, computers are faced with the intractable problem of how to represent nothing with something.

In Objective-C, there are several different varieties of nothing. The reason for this goes back to a common NSHipster refrain, of how Objective-C bridges the procedural paradigm of C with Smalltalk-inspired object-oriented paradigm.

C represents nothing as 0 for primitive values, and NULL for pointers (which is equivalent to 0 in a pointer context).

Objective-C builds on C's representation of nothing by adding nilnil is an object pointer to nothing. Although semantically distinct from NULL, they are technically equivalent to one another.

On the framework level, Foundation defines NSNull, which defines a class method, +null, which returns the singleton NSNull object. NSNull is different from nil or NULL, in that it is an actual object, rather than a zero value.

Additionally, in Foundation/NSObjCRuntime.h, Nil is defined as a class pointer to nothing. This lesser-known title-case cousin of nil doesn't show up much very often, but it's at least worth noting.

There's Something About nil

Newly-alloc'd NSObjects start life with their contents set to 0. This means that all pointers that object has to other objects begin as nil, so it's unnecessary to, for instance, set self.(association) = nil in init methods.

Perhaps the most notable behavior of nil, though, is that it can have messages sent to it.

In other languages, like C++, this would crash your program, but in Objective-C, invoking a method on nil returns a zero value. This greatly simplifies expressions, as it obviates the need to check fornil before doing anything:

// For example, this expression...if (name != nil && [name isEqualToString:@"Steve"]) { ... }// ...can be simplified to:if ([name isEqualToString:@"steve"]) { ... }

Being aware of how nil works in Objective-C allows this convenience to be a feature, and not a lurking bug in your application. Make sure to guard against cases where nil values are unwanted, either by checking and returning early to fail silently, or adding a NSParameterAssert to throw an exception.

NSNull: Something for Nothing

NSNull is used throughout Foundation and other frameworks to skirt around the limitations of collections like NSArray and NSDictionary not being able to contain nil values. You can think of NSNull as effectively boxing the NULL or nil value so that it can be used in collections:

NSMutableDictionary*mutableDictionary = [NSMutableDictionary dictionary];mutableDictionary[@"someKey"] = [NSNull null]; // Sets value of NSNull singleton for `someKey`NSLog(@"Keys: %@", [mutableDictionary allKeys]); // @[@"someKey"]

So to recap, here are the four values representing nothing that every Objective-C programmer should know about:

SymbolValueMeaningNULL(void *)0literal null value for C pointersnil(id)0literal null value for Objective-C objectsNil(Class)0literal null value for Objective-C classesNSNull[NSNull null]singleton object used to represent null

Comparing to NSNull

JAN 6TH, 2012

Checks to NSNull come up a lot when dealing with things like parsing JSON and while it’s mostly just ==, there are some options.

The officially sanctioned method is NSNull sample code, but this will generate a warning in clang (BOOO HISSSS).

So here are the options as I see them:

- (void)someMethod{    NSString *aString = @"loremipsum";        // This will complain: "Comparison of distinct pointer types ('NSString *' and 'NSNull *')"    if (aString != [NSNull null])    {            }        // This works (at least for strings), but isEqual: does different things     // for different classes, so it's not ideal    if ([aString isEqual:[NSNull null]])     {            }        // If you cast it to the class you're comparing against    // then you're good to go    if (aString != (NSString *)[NSNull null])    {            }        // But we can also just cast it to id and    // that works generically    if (aString != (id)[NSNull null])    {            }        // The thing that would be really cool,    // would be [NSNull null] returning    // id (like in the sample category below).    // Wouldn't count on that one though.    if (aString != [NSNull idNull])    {            }}
@interface NSNull (idNull)+ (id)idNull;@end@implementation NSNull (idNull)+ (id)idNull { return [NSNull null]; }@end

Casting to (id) seems like the simplest way to handle this one, so that’s the one I’m using right now.

Update:

One of my coworkers, @pgor, pointed out that he uses NSNull’s isEqual, which seems like a sensible enough thing to do. (I’m guessing Apple isn’t going to toss anything to screwy into an isEqual on an object that’s basically single purpose.)

if ([[NSNull null] isEqual:aString]) {        }

Also forgot to mention in the original post: it’s worth filing a bug against the documentation on NSNull to see what Apple’s official position is in this post static humiliator world we inhabit.


Ref:

1. nil / Nil / NULL / NSNull
2. Comparing to NSNull