Reputation: 489
So, here's the problem:
Upvotes: 2
Views: 1387
Reputation: 3294
I came up with another solution:
id value_from_json = json[key];
if([value_from_json isKindOfClass[NSNumber class]]) {
NSNumber *number_value = (NSNumber *)value_from_json;
// Observe that the check is "isEqualToValue", not "isEqualToNumber"
if([number_value isEqualToValue:@YES] || [number_value isEqualToValue:@NO]) {
// It's a boolean
} else {
// It's not a boolean
}
}
I have tested it and it can distingush between "true", true and 1. (and "false", false and 0)
Upvotes: 0
Reputation: 53000
Unfortunately there is no guarantee that you can determine the source type used to create an NSNumber
. The NSNumber
documentation states it is toll-free bridged with CFNumber
, in other words an instance of one can be treated as an instance of the other (with appropriate bridge casts in the case of ARC). The documentation for both types clearly states that while the type of the value stored in an instance of either can be determined, by CFNumberGetType()
or objCType
, this may not be the same type as the instance was constructed with. This is from the CFNumber
documentation:
The type specified in the call to
CFNumberCreate
is not necessarily preserved when a new CFNumber object is created—it uses whatever internal storage type the creation function deems appropriate.
If you examine CFNumberType
, an enum
to represent the type of value in a CFNumber
, then there is no value for BOOL
.
Further there is the type CFBoolean
to store wrapped booleans, it is not defined as a subtype of CFNumber
or mentioned in the documentation for either it or NSNumber
.
While it transpires that currently if you create an NSNumber
with a BOOL
value the result has been observed to be one of the constant values defined by CFBoolean
there is no documentation that states that this is the defined behaviour.
So while for curiosity sake you can try to figure it out, don't rely on any specific behaviour in production code.
Note: CFBoolean
is defined as one of the Core Foundation property list types. It may be possible to prove through using the various toll-free bridge relationships etc. that a CFBoolean
must be toll-free bridged to NSNumber
. However I do not think that guarantees to you that a boolean from JSON must be a CFBoolean
instance, though it probably is...
Upvotes: 1
Reputation: 130092
The answer is simple - don't.
Your data parser should know what value to expect on every place. Just don't mix numbers and booleans.
Upvotes: 1
Reputation: 489
tldr: Use kCFBooleanFalse & kCFBooleanTrue to detect booleans correctly!
NSNumber holding booleans are instances of __NSCFBoolean private class, which is a special beast - pointer to any instance of this class will be either kCFBooleanFalse or kCFBooleanTrue, which gives us ability to use this code(ARC):
if ([value isKindOfClass::[NSNumber class]])
{
if ((__bridge CFBooleanRef)value == kCFBooleanTrue)
{
// value == @YES
}
else if ((__bridge CFBooleanRef)value == kCFBooleanFalse)
{
// value == @NO
}
else
{
// value is other type of number (int, float, etc...)
}
}
Upvotes: 2