BS
BleepingSwift
Published on

> nil vs Nil vs NULL vs NSNull in Objective-C

Authors
  • avatar
    Name
    Mick MacCallum
    Twitter
    @0x7fs

Objective-C gives you four distinct ways to represent "nothing": nil, Nil, NULL, and NSNull. They look similar but serve different purposes, and confusing them leads to bugs that are surprisingly hard to track down.

nil: The Objective-C Object Pointer

nil represents a pointer to no object. It's defined as (id)0 and is what you use when dealing with Objective-C objects:

NSString *name = nil;
NSArray *items = nil;
id something = nil;

The magic of nil is that you can send messages to it without crashing. Messages to nil return zero (or the zero equivalent for the return type):

NSString *name = nil;
NSUInteger length = [name length];  // Returns 0
BOOL isEmpty = [name isEqualToString:@""];  // Returns NO (0)
NSString *upper = [name uppercaseString];  // Returns nil

This behavior simplifies code significantly. Instead of defensive nil checks everywhere, you can often let nil propagate naturally:

// This is fine even if user or user.profile is nil
NSString *bio = [[[user profile] bio] uppercaseString];

If any part of the chain is nil, the result is nil. No crash, no exception.

Nil: The Class Pointer

Nil (capital N) represents a pointer to no class. It's defined as (Class)0:

Class someClass = Nil;
Class stringClass = [NSString class];

if (someClass == Nil) {
    // No class assigned
}

In practice, you rarely use Nil directly. It's the correct choice when you have a variable of type Class rather than id, but most code deals with object instances, not classes. When you do work with classes dynamically (like in runtime manipulation), Nil is technically correct:

Class cls = NSClassFromString(@"NonexistentClass");
if (cls == Nil) {
    NSLog(@"Class not found");
}

That said, many codebases use nil here and the compiler doesn't complain. The distinction matters more for readability than correctness.

NULL: The C Pointer

NULL is the C null pointer, defined as (void *)0. Use it with C pointers, not Objective-C objects:

int *numbers = NULL;
char *buffer = NULL;
void *context = NULL;

You'll encounter NULL frequently when working with Core Foundation, Core Graphics, or any C API:

CGContextRef context = NULL;
CFStringRef cfString = NULL;

CGImageRef image = CGImageCreateWithJPEGDataProvider(
    provider,
    NULL,  // decode array
    true,  // shouldInterpolate
    kCGRenderingIntentDefault
);

In C APIs, passing NULL typically means "use the default" or "I don't need this parameter." Unlike nil, dereferencing NULL crashes immediately.

NSNull: Nothing as an Object

NSNull is fundamentally different from the others. It's not a zero value. It's an actual object that represents the concept of nothing:

NSNull *null = [NSNull null];  // A singleton object

Why would you need an object to represent nothing? Because Foundation collections can't contain nil:

// This crashes at runtime
NSArray *bad = @[@"one", nil, @"three"];

// This works
NSArray *good = @[@"one", [NSNull null], @"three"];

NSNull appears frequently when parsing JSON where null values exist:

NSDictionary *json = @{
    @"name": @"Alice",
    @"nickname": [NSNull null],  // JSON null
    @"age": @30
};

// Check for NSNull before using
id nickname = json[@"nickname"];
if (nickname == [NSNull null]) {
    NSLog(@"No nickname set");
}

Since NSNull is a real object, comparing with == works because it's a singleton. Every call to [NSNull null] returns the same instance.

Common Mistakes

Confusing NSNull with nil in collection checks:

// Wrong: This checks if the key exists, not if the value is null
if (dict[@"key"] == nil) {
    // Key might exist with NSNull value!
}

// Right: Check for both
id value = dict[@"key"];
if (value == nil || value == [NSNull null]) {
    // Key missing or explicitly null
}

Using NULL with Objective-C objects:

// Works but wrong type
NSString *name = NULL;

// Correct
NSString *name = nil;

Sending messages to NSNull expecting nil behavior:

id value = [NSNull null];
NSString *upper = [value uppercaseString];  // Crash! NSNull doesn't respond to uppercaseString

Unlike nil, NSNull is a real object. It only responds to NSObject methods and a few others. Sending an unrecognized message crashes.

The Practical Summary

Use nil for Objective-C object pointers. This is what you'll use 95% of the time.

Use Nil for Class pointers, though nil works too if you're not being pedantic.

Use NULL for C pointers: int *, char *, void *, and Core Foundation types like CGContextRef.

Use NSNull when you need to store "nothing" in a collection or represent an explicit null value from JSON or a database.

When receiving data from external sources (network, database, JSON), always check for both nil and NSNull:

- (NSString *)safeStringFromValue:(id)value {
    if (value == nil || value == [NSNull null]) {
        return nil;
    }
    if ([value isKindOfClass:[NSString class]]) {
        return value;
    }
    return [value description];
}

This pattern handles missing keys, explicit nulls, and type mismatches gracefully.

The four types of nothing in Objective-C reflect its heritage as a bridge between C and object-oriented programming. Understanding when to use each makes your code clearer and prevents the class of bugs where you're checking for the wrong kind of nothing.

subscribe.sh

// Stay Updated

Get notified when I publish new tutorials on Swift, SwiftUI, and iOS development. No spam, unsubscribe anytime.

>

By subscribing, you agree to our Privacy Policy.