14

Below is a code block, that is supposed to test to see if a dictionary is null, and if it isn't, pull out the correct object. However, for some reason, despite the fact that the if check fails, the code still executes. Is there some quirk with how NSNull works that I don't understand, or is this an Apple bug?

if (svcUser && !(svcUser == (id)[NSNull null])) {
    return [svcUser objectForKey:@"access_level"];
}

Console response:

(lldb) print svcUser && !(svcUser == (id)[NSNull null])
(bool) $0 = false
(lldb) continue
-[NSNull objectForKey:]: unrecognized selector sent to instance 0x2b51678
Eric Platon
  • 9,819
  • 6
  • 41
  • 48
jungziege
  • 245
  • 1
  • 4
  • 8

4 Answers4

31

Simply check for:

svcUser == [NSNull null]

This is the approach that Apple mentions in their docs.

JE42
  • 4,881
  • 6
  • 41
  • 51
20

NSNull is a class. And like with all classes, you must use isEqual:, not == to see if two objects represent the same value.

if (svcUser && ![svcUser isEqual:[NSNull null]]) {
    return [svcUser objectForKey:@"access_level"];
}
rmaddy
  • 314,917
  • 42
  • 532
  • 579
  • 7
    Yes, but `[NSNull null]` is a singleton object, so you can check it either way, since they will be reference equal. As you can tell from the console output, the `if` logic is fine, but somehow the code block is still getting executed. – jungziege May 17 '13 at 02:19
  • 1
    I feel like I've always used `==` successfully for `NSNull`. Apple's [sample code](http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/NumbersandValues/Articles/Null.html#//apple_ref/doc/uid/TP40005153-SW1) seems to even endorse this comparison. – Carl Veazey May 17 '13 at 02:20
  • 3
    But that is an implementation detail you shouldn't count on. Using `isEqual:` is better and safer than relying on `==` working. This has burned a lot of people in the past when comparing `NSString` literals. @jungziege - just a sanity check but are you 100% sure that the crash is in the posted line of code and not elsewhere? – rmaddy May 17 '13 at 02:22
  • I wouldn't say it's an implementation detail given that it's documented to be a singleton. For the sake of consistency it may be best to go with `isEqual:` though, especially if you call it on `[NSNull null]` and not your own object. Speaking of implementation details, `NSNull` doesn't seem to override `isEqual:` currently, so I'm skeptical that replacing `==` with `isEqual:` would produce a different result at the present time - I suspect that you're spot on with the sanity check and that the code is crashing elsewhere. – Carl Veazey May 17 '13 at 02:47
  • 3
    That seems to conflict with the Apple documentation "To test for a null object value, you must therefore make a direct object comparison." https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/NumbersandValues/Articles/Null.html – Rembrandt Q. Einstein Sep 30 '14 at 17:43
10

Using @JE42's approach gives me a warning as of Xcode 5.1. Instead cast it:

(id)svcUser == [NSNull null]
Liron Yahdav
  • 10,152
  • 8
  • 68
  • 104
8

You can check it by using:

 if(![svcUser isKindOfClass:[NSNull class]]){
    return [svcUser objectForKey:@"access_level"];
}
Joshua
  • 2,432
  • 1
  • 20
  • 29
  • 1
    If `svcUser` is actually an `NSNull` object, calling `count` on it will result in a crash. Also, the goal is to be sure it is *not* an `NSNull` object. And what is `nsnull`? – rmaddy May 17 '13 at 02:16
  • Also, using `==` to compare the two objects isn't correct. You need to use `isEqual:` to compare two objects. – rmaddy May 17 '13 at 02:18
  • sorry didn't noticed it. but already updated it. at first I thought you were looking checking if svcUser has value my bad – Joshua May 17 '13 at 02:25
  • This is my favored method, as it does not require typecasting – James Billingham May 09 '14 at 10:30
  • `if (svcUser != nil && ![svcUser isEqualTo:[NSNull null]])`, you should **check nil**, and checking NSNull is not enough. – DawnSong Jun 12 '15 at 02:52