What is the difference between [NSNull new] and [NSNull null]?

66 views Asked by At

I usually use [NSNull new] if I want a NSNull object, but today I noticed that NSNull class provided a class method [NSNull null] to create a NSNull object.

I wonder what is the difference between [NSNull new] and [NSNull null]? is it a wrong way to use [NSNull new] to create an NSNull object?

2

There are 2 answers

1
Mojtaba Hosseini On BEST ANSWER

By the documentation, [... new] equals [[... alloc] init] and creates and returns a new instance for any class that inherits from the NSObject.

[NSNull null] is a singleton instance that always returns the same instance which is the kCFNull. but if you see the documentation of the NSNull, you can see:

NSNull (itself) is a singleton object used to represent null values in collection objects that don’t allow nil values.

So because of the nature of the NSNull, all of them are the same by the instance:

NSNull* null = [NSNull null];
NSNull* init = [[NSNull alloc] init];
NSNull* new = [NSNull new];
NSNull* kcf = kCFNull;

Demo

Note how all of them are pointing to the exact same location

4
Rob Napier On

As Mojtaba notes, every NSNull is the same object. It's implemented this way:

CoreFoundation`+[NSNull allocWithZone:]:
    0x18cf7aeb8 <+0>:  adrp   x8, 368436
->  0x18cf7aebc <+4>:  add    x8, x8, #0x210            ; kCFNull
    0x18cf7aec0 <+8>:  ldr    x0, [x8]
    0x18cf7aec4 <+12>: ret    

When you call +[NSNull alloc] (which is called by +new), it just returns the singleton kCFNull. That is a toll-free bridged object, so can be used identically in CoreFoundation and Foundation. It's defined in CFBase as an empty CoreFoundation object:

static struct __CFNull __kCFNull = {
    INIT_CFRUNTIME_BASE()
};
const CFNullRef kCFNull = &__kCFNull;

All memory management calls are ignored:

                     -[NSNull autorelease]:
0000000180506ec8         ret
                     -[NSNull retain]:
0000000180506ecc         ret
                     -[NSNull release]:
0000000180506ed0         ret
                     -[NSNull retainCount]:
0000000180635104         mov        x0, #0xffffffffffffffff
0000000180635108         ret
                     -[NSNull dealloc]:
000000018063510c         ret

It's based on the "strict singleton" pattern in Cocoa. (Most of the time you don't want strict singletons, and we just create shared instances, but when you need there to be precisely one instance, this is how it's done.)

+[NSNull null] is implemented identically (it just returns kCFNull):

                     +[NSNull null]:
0000000180501558         adrp       x8, #0x1da43a000 ; 0x1da43a210@PAGE
000000018050155c         add        x8, x8, #0x210   ; 0x1da43a210@PAGEOFF, _kCFNull
0000000180501560         ldr        x0, [x8]         ; _kCFNull,___kCFNull
0000000180501564         ret

So +[NSNull null] is ever-so-slightly faster to call, since it skips some intermediate calls and (ignored) memory management. More importantly, it is better style to use +null rather than +new to make it clear that it's a singleton and you are using it that way.

In fact, the common way to check for NSNull is using == (pointer equality) rather than isEqual: (object equality), which is unusual in Cocoa.