You Can Pass ‘NO’ as a NSNumber, But You Probably Don’t Want To

When using Core Data, Boolean values as an attribute are represented using NSNumber. That’s all fine and good, but it requires you to use [NSNumber numberWithBool:YES|NO] when you set the value.

NO is defined as:

#define NO              (BOOL)0

we have an issue. 0 is also ‘nil’. Just below the definition of NO we see nil:

#define nil 0       /* id of Nil instance */

Since ‘nil’ has to be a valid NSNumber *, you are allowed to pass NO as a NSNumber * parameter. If object ‘foo’ has a method ’setBarOn’ with a declaration of

- (void) setBarOn:(NSNumber *)aBooleanAsANumber;
- (NSNumber *) barOn;

You need to make sure you never call that method with NO, as in:

[foo setBarOn:NO]; 

If you were to try to read the boolValue by hand you’d be OK. Calling ‘boolValue’ on a nil object seems to return 0, which is NO, so you wouldn’t notice the problem. The problem comes in when you use predicates.

I had a predicate that looked like:

“isBarOn == 0”

If you setBarOn:NO instead of setBarOn:[NSNumber numberWithBool:NO] the instance in question will not be returned by this predicate. Ouch. Fortunately if you were to look at the attribute that had been set to :NO you’ll see it reported as (null), which is a good hint your Boolean got set wrong.

I’m not sure how common this is or not, but it seemed worthy of pointing out. Hopefully it made some sense to someone.

Leave a Reply


© 2006 roobasoft, LLC