为什么需要NSNumber?
NSArray,NSDictionary等集合类,只能添加对象,如int a = 100
这样的纯值变量无法添加;
相对应的,NSUserDefaults,FMDB数据库等数据保存框架,也要求对象,无法保存纯值;
因此,我们常用的纯数值,需要一个“包装”,以方便其它类作为对象存储和转换,NSNumber 就是这样的类。
NSNumber是Foundation框架内置的类,它的类层级如下
- NSObject
- NSValue
- NSNumber
可以看出NSNumber继承自NSValue,提供数值对象,作为纯C类型(数值)的封装,包括有符号和无符号的char, short int, int, long int, long long int, float, double以及 BOOL值。
封包(值->对象)
使用字面量是最方便的封包方式:
NSArray *nums = @[@1, @2, @3]; //数值前加@标记即可
NSNumber *aNumber = @0xff; //可以使用16进制声明
@0.2f; // 声明浮点数
@YES; // 声明BOOL
int i = 100;
NSNumber *numObj = @(i); // 封装C变量
这里注意,16进制默认是无符号整形,如果需要符号数,只能使用:
NSNumber *aNumber = @0xff;
NSLog(@"%@", aCharObj.stringValue);
// 输出为`255`
NSNumber *aCharObj = [[NSNumber alloc] initWithChar:0xFF];
NSLog(@"%@", aCharObj.stringValue);
// 输出为`-1`
当然,有符号数可以直接用@-1
声明
解包 (对象->值)
每种数值类型都有对应的取值方法,形如boolValue
,这里有几个注意点:
-
intValue
和integerValue
并不完全等价,具体差别请关注笔者后续系列文章。 - 不同类型可交叉调用取值方法,隐含着精度转换,需要当心转换时的精度损失。如:
NSNumber *floatObj = @0.02;
NSLog(@"%d", [floatObj intValue]); // 得到的是0
不过不用担心比较时的精度丢失,因为int类型会转换为double类型再做比较,只要记住低精度默认转换为高精度即可。
比较
NSNumber *a = @1;
NSNumber *b = @1;
if (a > b) {
NSLog(@"a > b");
}
if (a < b){
NSLog(@"a < b");
}
if (a == b) {
NSLog(@"a == b");
}
用比较运算符可以正常比较两个NSNumber对象存储的值的大小,与调用比较方法是等价的:
if ( [a compare:b] == NSOrderedAscending) { //等价于 a < b
NSLog(@"a < b");
}
if ([a isEqualToNumber: b]) {
NSLog(@"a == b");
}
类型判断
正如上面解包时提到的,如果不知道一个NSNumber存储的原始类型,取值时就有可能损失精度,我们可以用以下方法避免:
- (const char *)objCType
NSLog(@"c type is %s", [@0.2f objCType]);
返回值是一个纯C字符串, 只有一个字符,含义如下:
字符 | 值类型 |
---|---|
'c' | char |
'i' | int |
's' | short |
'l' | long |
'q' | long long |
'C' | unsigned char |
'I' | unsigned int |
'S' | unsigned short |
'L' | unsigned long |
'Q' | unsigned long long |
'f' | float |
'd' | double |
以上类型不需要记忆,使用 @enable即可,这里提供一个判断NSNumber 类型的Sample:
if ([value isKindOfClass:[NSNumber class]]) {
if (strcmp([value objCType], @encode(float)) == 0) {
[cell.detailTextLabel.text = [NSString stringWithFormat:@"%.3f", [value floatValue]]];
}
else
if (strcmp([value objCType], @encode(double)) == 0) {
[self.subTitleString appendString:[NSString stringWithFormat:@"%.3f", [value floatValue]]];
}
else
if (strcmp([value objCType], @encode(int)) == 0) {
[self.subTitleString appendString:[NSString stringWithFormat:@"%d", [value intValue]]];
}
else
[self.subTitleString appendString: [NSString stringWithFormat:@"%d", [value intValue]]];
}
NSNumber 不能做什么
无法做计算,如 a + b
小技巧
利用stringValue
快速转换数值为字符串
// 假设已获得NSIndexPath类型的indexPath值
UILabel *titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0.0, 0.0, 60.0, 30.0)];
titleLabel.text = @(indexPath.row).stringValue;
你可以尝试用NSString 的stringWithFormat
方法完成同样的转换,比较其中差异。
网友评论