0

In Objective C, a common practice is to often forgo checking for nil, instead relying on the fact that messaging nil silently fails.

For example, to validate a string:

if ([myString length] > 1) { // Short and sweet

If myString is nil, this check will fail - as intended. It's a huge improvement over what one may assume would be the correct implementation, explicitly checking for nil:

if (myString && [myString length] > 1) { // Redundant nil check

However, particularly in the case of a web API, myString could conceivable equal [NSNull null] - similar to nil, but with different behavior. Of primary concern, is that messaging NSNull causes an exception. For example, our short simple solution from earlier will cause an exception:

// Causes an exception, because [NSNull null] is an object
NSString *myString = (id)[NSNull null];
if ([myString length] > 1) { 

To further complicate things, a simple nil check will actually pass because [NSNull null] is a valid object:

NSString *myString = (id)[NSNull null];
if (myString) { // Evals to YES

So, the only way to be completely safe is to both check for NSNull and do your regular implicit nil check, by calling a method:

if (myString != (id)[NSNull null] && [myString length] > 1) { // Short and sweet

My Question: is this really necessary? Is there a more concise option with less duplicate code, that I've overlooked? Are my conclusions, in fact, correct?

emma ray
  • 13,336
  • 1
  • 24
  • 50
  • if (mystring != [NSNull null]) { // Do something for a null } Try this code and and no need covert id – karthikeyan viswanathan Dec 16 '14 at 16:47
  • Unclear what "better" could mean in any objective sense. Stack Overflow is not a place for touchy-feely answers. Obviously if something could be a NSNull and you need to know that, you need to test for that possibility. So the question is meaningless. – matt Dec 16 '14 at 17:09

4 Answers4

5

NSNull objects typically crop up with people reading JSON files containing null values.

In that case, there is a chance that the server that supplied the null value thinks you should do something different than with no value. For example, if you get a dictionary and there might be a string stored under some key, you might get a string with non-zero length, you might get an empty string because the server sent you an empty string, you might get nothing because the server sent you nothing, or you might get [NSNull null] because the server sent you a null value. Up to you to decide if the empty string, nothing, and [NSNull null] need to be treated differently or not.

By the way: I have always used some extensions to the NSDictionary class, for example - (NSString*)stringForKey, which will return either an NSString or nil, 100% guaranteed. If you do that, all your error checking is in one method. And you can do things like converting NSNumber to NSString, or NSNull to nil, or NSDictionary etc. to nil, and never worry about it again. Of course you need to check if it is appropriate.

gnasher729
  • 51,477
  • 5
  • 75
  • 98
2

is this really necessary?

If some API can return NSNull instead of a string, then yes, you should check for it. It's usually not necessary since NSNull is not a string and won't be returned by most methods that would return a string. But a collection such as a dictionary, for example, can have any object as a value. If you're not sure that the value will be a string, you should take appropriate precautions.

Is there a better way to handle the possibility of NSNull that I've overlooked?

Another way that you could do roughly the same thing but protect yourself from other types (NSNumber, for example) is to check the type of the thing that you get back rather than comparing specifically against [NSNull null]:

if ([myString isKindOfClass:[NSString class]] && [myString length] > 1) { //...
Caleb
  • 124,013
  • 19
  • 183
  • 272
  • Good details, I'll definitely replace my Null check with introspection. However, I'm going to add it either in my API logic or as an NSDictionary/Array extension, to centralize all my checking. – emma ray Dec 16 '14 at 18:13
1

In addition to the other answers, you could consider replacing NSNull instances with empty strings before parsing your API response into model objects. Similarly, you could traverse a mutable copy of the response dictionary and remove keys whose values contain NSNull.

Basically, do it up front when you get the response so that your code isn't littered with NSNull checks.

Community
  • 1
  • 1
Aaron Brager
  • 65,323
  • 19
  • 161
  • 287
0

Another way to approach this is to use a Category:

@interface NSNull (Utilities)

- (NSString *)stringValue;

@end

@implementation NSNull (Utilities)

- (NSString *)stringValue
{
    return nil;
}

@end

And then simply:

[myDictionary[@"key"] stringValue] // returns nil for NSNull values
gabrielmaldi
  • 2,157
  • 2
  • 23
  • 39