Search code examples
iosobjective-ckvc

BOOL property from a calculation returns NSNumber with incorect value using valueForKey:


I have a simple object which has one NSNumber which is used to store some flags. I have a conienience getter method which in fact does:

[self.flags integerValue] & SomeConstantFlag

for a property@property (readonly, nonatomic, assign) BOOL someConstantFlag

and this works fine when accesing the underlying bool value like

model.someConstantFlag

but when I try to

id value = [model valueForKey:@"someConstantFlag"];

Then it returns a bad boolean representation e.g. NSNumber with value 2, 4 etc. Why is this happening when the declaration of the property is BOOL? Is there a "Pretty" way to overcome this issue?

Wrapping on the other hand works ok:

BOOL someBool = 42;
NSNumber* numberVal = @(someBool);
//The underlying is an __NSCFBoolean with the proper 0/1 val!

Solution

  • valueForKey always returns an Objective-C object, even if the property has scalar type.

    From the documentation (emphasis mine):

    The default implementations of valueForKey: and setValue:forKey: provide support for automatic object wrapping of the non-object data types, both scalars and structs.

    Once valueForKey: has determined the specific accessor method or instance variable that is used to supply the value for the specified key, it examines the return type or the data type. If the value to be returned is not an object, an NSNumber or NSValue object is created for that value and returned in its place.

    The return value of your method is BOOL, which is defined as

    typedef signed char BOOL;
    

    on OS X and on the 32-bit iOS platform. So what valueForKey returns is a NSNumber containing the result of

    signed char val = [self.flags integerValue] & SomeConstantFlag;
    

    and that can be in the range -128 .. 127.

    To ensure that you get only YES or NO (aka 1 or 0) write your custom getter as:

    -(BOOL)someConstantFlag
    {
        return ([self.flags integerValue] & SomeConstantFlag) != 0;
    }
    

    Remark: On the 64-bit iOS platform (but not on 64-bit OS X), BOOL is defined as the C99 _Bool, which is a "proper" boolean type and can take only the value 0 or 1.