Reputation: 2153
I want to get the type of NSNumber instance.
I found out on http://www.cocoadev.com/index.pl?NSNumber this:
NSNumber *myNum = [[NSNumber alloc] initWithBool:TRUE]; if ([[myNum className] isEqualToString:@"NSCFNumber"]) { // process NSNumber as integer } else if ([[myNum className] isEqualToString:@"NSCFBoolean"]) { // process NSNumber as boolean }
Ok, but this doesn't work, the [myNum className] isn't recognized by the compiler. I'm compiling for iPhone.
Upvotes: 43
Views: 25197
Reputation: 15579
NSNumber
is a class-cluster so each underlying type can be figured from the instance. This code avoids hard-coding the different NSNumber types by creating an instance of the expected type, and then comparing it against the unknown type.
extension NSNumber {
var isBool: Bool {
return type(of: self) == type(of: NSNumber(booleanLiteral: true))
}
}
Upvotes: -1
Reputation: 4977
objCType
documentation states that The returned type does not necessarily match the method the number object was created with
Secondly, other methods of comparing the class of number to a given class type or assuming boolean number instances to be shared singletons are not documented behaviour.
A more(not completely though) reliable way is to depend on NSJSONSerialisation as it correctly recognises number instances created with bool and outputs true/false in json. This is something we can expect Apple to take care of while moving with new SDKs and on different architectures. Below is the code:
+(BOOL) isBoolType:(NSNumber*) number {
NSError* err;
NSData* jsonData = [NSJSONSerialization dataWithJSONObject:@{@"key":number}
options:0
error:&err];
NSString* jsonString = [[NSString alloc]
initWithData:jsonData
encoding:NSUTF8StringEncoding];
return [jsonString containsString:@"true"]
|| [jsonString containsString:@"false"];
}
Upvotes: -1
Reputation: 10185
In Swift:
let numberType = CFNumberGetType(answer)
switch numberType {
case .charType:
//Bool
case .sInt8Type, .sInt16Type, .sInt32Type, .sInt64Type, .shortType, .intType, .longType, .longLongType, .cfIndexType, .nsIntegerType:
//Int
case .float32Type, .float64Type, .floatType, .doubleType, .cgFloatType:
//Double
}
Upvotes: 7
Reputation: 860
To check that NSNumber contains a bool value Try this:
if (strcmp([myNumber objCType], [@(YES) objCType]) == 0)
NSLog(@"%@", [myNumber boolValue] ? @"true" : @"false");
Upvotes: 0
Reputation: 1026
Use the method -[NSNumber objCType] method
to get the type.
If the type's equal to @encode(BOOL), or the number itself is kCFBooleanFalse, or kCFBooleanTrue, it's a boolean.
If it's anything else but 'c', it's a number.
If it's 'c', what appears to be the only way supported way, without checking against private class names, or comparing against undocumented singletons, is to turn make an array of one element, the number, and then use NSJSONSerialization to get the string representation. Finally, check if the string representation contains the string "true" or "false". Here is the full code for checking if an NSNumber is a BOOL:
-(BOOL)isBool
{
if(!strcmp(self.objCType, @encode(BOOL)) ||
self == (void*)kCFBooleanFalse ||
self == (void*)kCFBooleanTrue)
{
return YES;
}
if(strcmp(self.objCType, "c"))
{
return NO;
}
NSString * asString = [[NSString alloc] initWithData:[NSJSONSerialization dataWithJSONObject:@[self] options:kNilOptions error:nil] encoding:NSUTF8StringEncoding];
return [asString containsString:@"true"] || [asString containsString:@"false"];
}
Note that using NSJSONSerialization is slow and if @NO/@YES ever stops always equalling kCFBooleanFalse/kCFBooleanTrue, then this method probably shouldn't be used in a tight loop.
Upvotes: 5
Reputation: 676
check object is of NSNumber type :
if([obj isKindOfClass:NSClassFromString(@"__NSCFNumber")])
{
//NSNumber
}
Upvotes: -2
Reputation: 12031
If all you want is to differentiate between booleans and anything else, you can make use of the fact that boolean NSNumbers always return a shared instance:
NSNumber *num = ...;
if (num == (void*)kCFBooleanFalse || num == (void*)kCFBooleanTrue) {
// num is boolean
} else {
// num is not boolean
}
Upvotes: 30
Reputation: 40623
You can get the type this way, no string comparisons needed:
CFNumberType numberType = CFNumberGetType((CFNumberRef)someNSNumber);
numberType will then be one of:
enum CFNumberType {
kCFNumberSInt8Type = 1,
kCFNumberSInt16Type = 2,
kCFNumberSInt32Type = 3,
kCFNumberSInt64Type = 4,
kCFNumberFloat32Type = 5,
kCFNumberFloat64Type = 6,
kCFNumberCharType = 7,
kCFNumberShortType = 8,
kCFNumberIntType = 9,
kCFNumberLongType = 10,
kCFNumberLongLongType = 11,
kCFNumberFloatType = 12,
kCFNumberDoubleType = 13,
kCFNumberCFIndexType = 14,
kCFNumberNSIntegerType = 15,
kCFNumberCGFloatType = 16,
kCFNumberMaxType = 16
};
typedef enum CFNumberType CFNumberType;
Upvotes: 51
Reputation: 99
NSNumber explicitly doesn't guarantee that the returned type will match the method used to create it, so doing this at all is probably a bad idea.
However, you could probably do something like this (you could also compare to objc_getClass("NSCFNumber")
etc., but this is arguably more portable):
Class boolClass = [[NSNumber numberWithBool:YES] class];
/* ... */
if([myNum isKindOfClass:boolClass]) {
/* ... */
}
Upvotes: 9
Reputation: 44769
The reason the compiler warns you and it doesn't work is because -[NSObject className]
is declared in a category on NSObject on Mac OS X (in NSScriptClassDescription.h) and not declared on iPhone. (It doesn't support AppleScript, obviously.) NSStringFromClass([myNum class])
is what you should use to be safe across all platforms. Odds are that -className
is declared as a simple wrapper around NSStringFromClass()
anyway...
Upvotes: 4
Reputation: 243146
I recommend using the -[NSNumber objCType]
method.
It allows you to do:
NSNumber * n = [NSNumber numberWithBool:YES];
if (strcmp([n objCType], @encode(BOOL)) == 0) {
NSLog(@"this is a bool");
} else if (strcmp([n objCType], @encode(int)) == 0) {
NSLog(@"this is an int");
}
For more information on type encodings, check out the Objective-C Runtime Reference.
Upvotes: 75
Reputation: 187004
NSString *classString = NSStringFromClass([myNum class]);
That should ger the string you want.
Upvotes: 3