Reputation: 359
Im checking to see if an array is nil and if it is I want to set some counter variable to 0 and to the length of the array if the array is not nil.
NSUInteger count = 0;
if (self.someArray == nil) {
count = 0;
}
else {
count = self.someArray.count;
}
and this works fine just as expected. However when I try to do something like this:
NSUInteger count = (self.someArray == nil) ? 0 : self.someArray.count
I get an [NSNull count] unrecognized selector
error. However, when I do this
([self.someArray isKindOfClass:[NSNull class]]) ? 0 : self.someArray.count`
It seems like the latter should work to me but is there something simple that I am overlooking? NSNull
is for objects that are nil right? Why is the simple nil check working in the if statement and not in the ternary operator?
Upvotes: 0
Views: 1362
Reputation: 62686
nil
is really (void *)0
. NSNull
is a class that has a single instance which you can get with [NSNull null]
. It is legal and harmless to send messages to nil, but messaging null is subject to run-time type checking.
So lets initialize an array to the NSNull object. Any code that starts out this way is sure to cause sadness at runtime, but this is exactly what's happening when you deserialize JSON (it has to do something with nulls found in the JSON)...
NSArray *array = (NSArray *)[NSNull null]; // evil cast, just to illustrate
if (array == 0)
NSLog(@"I will never execute");
else
NSLog(@"I will always execute. null is a pointer to the instance of NSNull");
if ([array isKindOfClass:[NSNull class]]) // true, of course
if (array == [NSNull null]) // true, null is a singleton
if (!array) // false, same as above
if (array == nil) // false, same as above, array points to an object
NSLog(@"CRASH on next line, because null doesn't respond to count");
if (array.count > 0) // crash
But if we try the some thing starting with the more benign nil
...
NSArray *array = nil;
// or NSArray *array; same thing, since compiler will init to 0x0 for you
if (array == 0)
NSLog(@"I will always execute");
else
NSLog(@"I will never execute");
if (!array) // true, same as above
if (array == nil) // true, of course
NSLog(@"No crash on next line");
if (array.count > 0)
NSLog(@"I will never execute");
That last bit is interesting. We can send the count message to a nil array without penalty. It answers 0, as if the array is empty. What's really happening is that we can send any message to nil, and the result will always be nil.
So what to do about your problem? One idea is to accept that self.array can sometimes be == [NSNull null]
(which, again, is not the same as == nil
). The better solution is to catch the JSON-produces-NSNull condition at the point of assignment, like this...
// I just finished deserializing some JSON to id someObject
self.someArray = (someObject.theArray == [NSNull null])? nil : someObject.theArray;
// anytime after, we can safely message the array
NSInteger theCount = self.someArray.count; // zero, if someArray is empty OR nil
// and even get objects from it, as long as it contains any
id someObject = (self.someArray.count)? [someArray lastObject] : nil;
Upvotes: 1
Reputation: 122391
self.someArray
is an NSNull
object, not an NSArray
object.
I suspect you got the object from a JSON message and haven't checked its type before storing it in your class.
Once you have that sorted; this works if the array is set or not:
NSUInteger count = self.someArray.count;
Upvotes: 1