Search code examples
objective-cnullnsnull

How to detect if NSString is null?


I have a piece of code that detects if a NSString is NULL, nil, etc. However, it crashes. Here is my code:

NSArray *resultstwo = [database executeQuery:@"SELECT * FROM processes WHERE ready='yes' LIMIT 0,1"];
for (NSDictionary *rowtwo in resultstwo) {

NSString *getCaption = [rowtwo valueForKey:@"caption"];

if (getCaption == NULL) {
theCaption = @"Photo uploaded...";
} else if (getCaption == nil) {
theCaption = @"Photo uploaded...";
} else if ([getCaption isEqualToString:@""]) {
theCaption = @"Photo uploaded...";
} else if ([getCaption isEqualToString:@" "]) {
theCaption = @"Photo uploaded...";
}

}

And here's the error:

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSNull isEqualToString:]: unrecognized selector sent to instance 0x3eba63d4'

Am I doing something wrong? Do I need to do it a different way?


Solution

  • The NULL value for Objective-C objects (type id) is nil.

    While NULL is used for C pointers (type void *).

    (In the end both end up holding the same value (0x0). They differ in type however.)

    In Objective-C:

    • nil (all lower-case) is a null pointer to an Objective-C object.
    • Nil (capitalized) is a null pointer to an Objective-C class.
    • NULL (all caps) is a null pointer to anything else (C pointers, that is).
    • [NSNull null] is a singleton for situations where use of nil is not possible (adding/receiving nil to/from NSArrays e.g.)

    In Objective-C++:

    • All of the above, plus:
    • null (lowercase) or nullptr (C++11 or later) is a null pointer to C++ objects.

    So to check against nil you should either compare against nil (or NULL respectively) explicitly:

    if (getCaption == nil) ...
    

    or let ObjC / C do it implicitly for you:

    if (!getCaption) ...
    

    This works as every expression in C (and with Objective-C being a superset thereof) has an implicit boolean value:

    expression != 0x0 => true
    expression == 0x0 => false
    

    Now when checking for NSNull this obviously wouldn't work as [NSNull null] returns a pointer to a singleton instance of NSNull, and not nil, and therefore it is not equal to 0x0.

    So to check against NSNull one can either use:

    if ((NSNull *)getCaption == [NSNull null]) ...
    

    or (preferred, see comments):

    if ([getCaption isKindOfClass:[NSNull class]]) ...
    

    Keep in mind that the latter (utilising a message call) will return false if getCaption happens to be nil, which, while formally correct, might not be what you expect/want.

    Hence if one (for whatever reason) needed to check against both nil/NULL and NSNull, one would have to combine those two checks:

    if (!getCaption || [getCaption isKindOfClass:[NSNull class]]) ...
    

    For help on forming equivalent positive checks see De Morgan's laws and boolean negation.

    Edit: NSHipster.com just published a great article on the subtle differences between nil, null, etc.