How do I check if a NSNumber object is nil or empty?
OK nil is easy:
NSNumber *myNumber;
if (myNumber == nil)
doSomething
But if th
NSNumber
s are immutable and can only be created with either a factory method or initial method that gives them some numeric value. As far as I know it is not possible to end up with an 'empty' NSNumber, unless you count 0.
Dealing with Swift 2.0 and Parse:
var myNumber = yourArray!.objectForKey("yourColumnTitle")
if (myNumber == nil) {
myNumber = 0
}
In my case, I then had to:
let myNumberIntValue = myNumber!.intValue
It's very common (for me at least) to get an object out of a dictionary, expect that it's going to be an NSNumber and then have it return a nil object. If this happens and you do an intValue it will crash.
What I do is setup nil protection because I'd rather get a default value than a crash.
One way is:
-(int) intForDictionary:(NSDictionary *)thisDict objectForKey: (NSString *)thisKey withDefault: (int)defaultValue
{
NSNumber *thisNumber = [thisDict objectForKey:thisKey];
if (thisNumber == nil) {
return defaultValue;
}
return [thisNumber intValue];
}
And I have one just like it for floats. Then you at least get your default value. Another way is to just create a method for nil protection..
-(NSNumber *)nilProtectionForNumber: (NSNumber *)thisNumber withDefault: (NSNumber *)defaultNumber
{
if (thisNumber) {
return thisNumber;
}
else
return defaultNumber;
}
That one you'd call like this:
NSNumber *value = [self nilProtectionForNumber:[dict objectForKey:keyThatShouldBeNSNumber] withDefault:[NSNumber numberWithInt:0]];
NSValue
, NSNumber
, ... are supposed to be created from a value and to always hold one. Testing for a specific value like 0
only works if it isn't in the range of valid values you are working with.
In the rare case where code is more straight-forward to work with if you have a value that represents "invalid" or "not set" and you can't use nil
(e.g. with the standard containers) you can use NSNull instead.
In your first example this could be:
[dict setValue:[NSNull null] forKey:@"emptyValue"];
if ([dict objectForKey:@"emptyValue"] == [NSNull null]) {
// ...
}
But note that you can simply not insert (or remove) that value unless you need to differentiate nil
(i.e. not in the container) and, say, "invalid":
if ([dict objectForKey:@"nonExistent"] == nil) {
// ...
}
As for the second example, -intValue
gives you 0
- but simply because sending messages to nil
returns 0
. You could also get 0
e.g. for a NSNumber
whose intValue
was set to 0
before, which could be a valid value.
As i already wrote above, you can only do something like this if 0
is not a valid value for you. Note the for you, what works best completely depends on what your requirements are.
Let me try to summarize:
Option #1:
If you don't need all values from the numbers range, you could use one (0
or -1
or ...) and -intValue
/ ... to specifically represent "empty". This is apparently not the case for you.
Option #2:
You simply don't store or remove the values from the container if they are "empty":
// add if not empty:
[dict setObject:someNumber forKey:someKey];
// remove if empty:
[dict removeObjectForKey:someKey];
// retrieve number:
NSNumber *num = [dict objectForKey:someKey];
if (num == nil) {
// ... wasn't in dictionary, which represents empty
} else {
// ... not empty
}
This however means that there is no difference between keys that are empty and keys that never exist or are illegal.
Option #3:
In some rare cases its more convenient to keep all keys in the dictionary and represent "empty" with a different value. If you can't use one from the number range we have to put something differently in as NSNumber
doesn't have a concept of "empty". Cocoa already has NSNull
for such cases:
// set to number if not empty:
[dict setObject:someNumber forKey:someKey];
// set to NSNull if empty:
[dict setObject:[NSNull null] forKey:someKey];
// retrieve number:
id obj = [dict objectForKey:someKey];
if (obj == [NSNumber null]) {
// ... empty
} else {
// ... not empty
NSNumber *num = obj;
// ...
}
This option now allows you to differentiate between "empty", "not empty" and "not in the container" (e.g. illegal key).
NSNumber
is either nil
, or it contains a number, nothing in between. “Emptiness” is a notion that depends on the semantics of the particular object and therefore it makes no sense to look for a general emptiness check.
As for your examples, there are several things going on:
NSMutableDictionary *hash = [NSMutableDictionary dictionary];
[hash setObject:@"" forKey:@"key"];
NSNumber *number = [hash objectForKey:@"key"];
NSLog(@"%i", [number intValue]);
The NSLog
will print 0
here, but only because there’s an intValue
method in NSString
. If you change the message to something that only NSNumber
can do, the code will fail:
NSLog(@"%i", [number unsignedIntValue]);
This will throw:
-[NSCFString unsignedIntValue]: unrecognized selector sent to instance 0x303c
Which means you are not getting some general “empty” value back from the hash, you just get the NSString
you stored there.
When you have an empty (== nil
) NSNumber
and send it a message, the result will be zero. That’s simply a language convention that simplifies code:
(array != nil && [array count] == 0)
(someNumber == nil ? 0 : [someNumber intValue])
Will turn into this:
([array count] == 0)
([someNumber intValue])
Hope this helps.