检查NSNumber是否为空。

19

我该如何检查 NSNumber 对象是否为空或 nil?

好的,判断 nil 很简单:

NSNumber *myNumber;
if (myNumber == nil)
    doSomething

但是,如果对象已经创建,但因为赋值失败而没有值,我该如何检查这一点?使用类似这样的东西吗?

if ([myNumber intValue]==0)
   doSomething

是否有一种通用方法可以像NSString那样测试对象的空值(参见此文章)?

示例1

NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
[dict setValue:@"" forKey:@"emptyValue"];
NSNumber *emptyNumber = [dict objectForKey:@"emptyValue"];
< p >emptyNumber 包含哪个值?我该如何检查emptyNumber是否为空?

示例2

NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
[dict setValue:@"" forKey:@"emptyValue"];
NSString *myString = [dict objectForKey:@"emptyValue"];
if (myString == nil || [myString length] == 0)
    // got an empty value
    NSNumber *emptyNumber=nil;

如果在将emptyNumber设置为nil后我还使用它会发生什么?

[emptyNumber intValue]

我会得到零吗?

示例3

NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
[dict setValue:@"" forKey:@"emptyValue"];
NSNumber *myEmptyValue = [dict objectForKey:@"emptyValue"];
if (myEmptyValue == nil)
    // NSLog is never called
    NSLog(@"It is empty!");

使用这种方式,NSLog永远不会被调用。myEmptyValue 不是 nil 也不是 NSNull。那么它包含一个任意数字?


@[0.7] 将返回一个绝对不为零的 NSNumber,但 intValue 将返回 0。所以这是一个错误的测试。 - gnasher729
因为详细的解释,我已经点赞了。NSNumber* savedPin = [[NSUserDefaults standardUserDefaults] valueForKey:@"someKey"]; 返回一个类为__NSCFConstantString的对象,因此与nil的比较失败了。谢谢! - Andy Dent
5个回答

13

NSValueNSNumber等应该从一个值创建,并且始终只持有一个值。如果要测试特定的值,如0,则仅在它不在您正在使用的有效值范围内时起作用。

在极少数情况下,如果您有一个表示"无效""未设置"的值,并且不能使用nil(例如与标准容器一起使用),则可以使用NSNull

在您的第一个示例中,可以这样做:

[dict setValue:[NSNull null] forKey:@"emptyValue"];

if ([dict objectForKey:@"emptyValue"] == [NSNull null]) {
    // ...
}

但请注意,除非您需要区分nil(即不在容器中)和"无效",否则可以简单地不插入(或删除)该值:

if ([dict objectForKey:@"nonExistent"] == nil) {
    // ...
}

关于第二个示例,-intValue 返回 0,但仅因为向 nil 发送消息会返回 0。你也可以获得 0,例如对于一个先前设置了 intValue0NSNumber,这可能是一个有效的值。
正如我之前写过的那样,只有当 0 对你不是一个有效的值时,你才能像这样做。请注意“对你来说”,最好完全取决于你的要求。
让我试着总结一下:
选项 #1:
如果您不需要数字范围中的所有值,则可以使用一个(0-1 等)和 -intValue / ... 来表示 “空”。显然,这对你不适用。
选项 #2:
如果这些值是“空”的,您可以简单地不存储或删除容器中的这些值:
// add if not empty:
[dict setObject:someNumber forKey:someKey];    
// remove if empty:
[dict removeObjectForKey:someKey];
// retrieve number:
NSNumber *num = [dict objectForKey:someKey];
if (num == nil) {
    // ... wasn't in dictionary, which represents empty
} else {
    // ... not empty
}

这意味着空键和从未存在或非法的键之间没有区别。
选项#3:
在某些罕见情况下,将所有键保留在字典中并使用不同的值表示“空”更为方便。如果您无法使用数字范围中的一个,则必须输入其他内容,因为NSNumber没有“空”的概念。 Cocoa已经为此类情况准备了NSNull
// set to number if not empty:
[dict setObject:someNumber forKey:someKey];
// set to NSNull if empty:
[dict setObject:[NSNull null] forKey:someKey];
// retrieve number:
id obj = [dict objectForKey:someKey];
if (obj == [NSNumber null]) {
    // ... empty
} else { 
    // ... not empty
    NSNumber *num = obj;
    // ...
}

现在,此选项使您能够区分"空""非空""不在容器中"(例如,非法键)。


你正在插入 [NSNull null] 以便稍后测试 NSNumber 对象是否为空。好的,但是标准容器是什么?我无法保证我的 NSNumber 对象被分配了哪个值。因此,我不知道何时插入 nilNSNull。在我的示例中,它也可能是空字符串。我应该先测试 objectForKey 的返回值是否为空字符串,然后再将 nilNSNull 插入我的 NSNumber 对象吗?我认为我应该始终插入 nil,除非我需要 NSNull 用于某些情况。请参见我的第二个示例。 - testing
1
@testing:标准容器是Cocoa Touch中的容器(NSArrayNSDictionary等)。第一个示例测试对象是否为NSNull,第二个示例假设您没有将值放入容器中 - 您无法在其中插入nilnil表示“不在容器中”。您只需决定是否需要区分“无效”/“不存在”和“不在容器中”。您最后一句话接近我的说法-要么不将其放入容器中(这样您就会得到-objectForKey:...nil),要么使用NSNull - Georg Fritzsche
1
@testing: 关于“所以我不知道何时插入nil或NSNull”的问题 - 你必须在某个地方知道,对象并不会自动设置为特定的值。无论是你在代码中的某个地方知道你是否正在处理“无效”或“空”值,还是你做错了。 - Georg Fritzsche
@Georg:所以理解上的问题是:我的英语水平;NSArray/NSDictionary/...中不允许使用nil;不清楚nil和NSNull之间的区别;从字典中获取到未知返回值,导致我开始将NSNumber与其他东西混合使用。问题: 1)容器中同时包含nil和NSNull行不通,因为它们的含义不同,但如果检查是否为nil,则结果相同? 2)你的意思是“可以在其中插入nil”吗? - testing
@Georg:我使用0,所以我无法检查它是否为零。 只有当我有一个数组,无法在其中放置nil值时,才使用“不在容器中”。因此,我的解决方案是: 使用来自这个问题的isEmpty。您必须将其转换为另一种数据类型,以检查未知返回值是否具有长度零(由isEmpty或类似于示例2完成)。对于NSNumber,请使用nil进行检查(对于标准容器,请检查NSNull)。 - testing
显示剩余3条评论

3
NSNumber可以是nil,也可以包含一个数字,没有其他内容。 “空值”是一个依赖于特定对象语义的概念,因此寻找一般的空值检查是没有意义的。
至于你的例子,有几件事情正在发生:
NSMutableDictionary *hash = [NSMutableDictionary dictionary];
[hash setObject:@"" forKey:@"key"];
NSNumber *number = [hash objectForKey:@"key"];
NSLog(@"%i", [number intValue]);

NSLog 会打印 0,但这只是因为 NSString 中有一个 intValue 方法。如果你把消息改成只有 NSNumber 才能做的事情,代码将会失败:

NSLog(@"%i", [number unsignedIntValue]);

这将会抛出异常:
-[NSCFString unsignedIntValue]: unrecognized selector sent to instance 0x303c

这意味着你不会从哈希表中获得一些通用的“空”值,而是获得你存储在那里的NSString。当你有一个空(== nil)的NSNumber并发送消息时,结果将为零。这只是一种简化代码的语言约定:
 (array != nil && [array count] == 0)
 (someNumber == nil ? 0 : [someNumber intValue])

将变成这样:

 ([array count] == 0)
 ([someNumber intValue])

希望这能帮到你。

那么 NSNumber *test = [NSNull null] 是什么意思? - Bradley Thomas
1
就像代码所写的那样,它并没有太多意义。NSNull只是一个“nil值的对象版本”,它存在的唯一原因是为了在不能直接存储nil的集合中表示一个“空”值。 - zoul
谢谢,是的,我想我只是在努力清楚地了解NSNumber需要考虑的可能性范围。 - Bradley Thomas

1
处理Swift 2.0和Parse:
var myNumber = yourArray!.objectForKey("yourColumnTitle")
    if (myNumber == nil) {
    myNumber = 0
      }

在我的情况下,我接着必须:
    let myNumberIntValue = myNumber!.intValue

0

对我来说,从字典中获取一个对象并期望它是NSNumber类型,但最终返回nil对象是非常常见的。如果发生这种情况,并且您执行intValue操作,程序将会崩溃。

为了避免崩溃,我通常会设置nil保护机制,以便在出现问题时能够获得默认值而不是崩溃。

其中一种方法是:

-(int) intForDictionary:(NSDictionary *)thisDict objectForKey: (NSString *)thisKey withDefault: (int)defaultValue
{
    NSNumber *thisNumber = [thisDict objectForKey:thisKey];
    if (thisNumber == nil) {
        return defaultValue;
    }
    return [thisNumber intValue];
}

我还有一个类似的针对浮点数的方法。这样你至少可以得到默认值。另一种方法是创建一个用于nil保护的方法。

-(NSNumber *)nilProtectionForNumber: (NSNumber *)thisNumber withDefault: (NSNumber *)defaultNumber
{
    if (thisNumber) {
        return thisNumber;
    }
    else
        return defaultNumber;
}

你可以这样调用它:

NSNumber *value = [self nilProtectionForNumber:[dict objectForKey:keyThatShouldBeNSNumber] withDefault:[NSNumber numberWithInt:0]];

0

NSNumber是不可变的,只能通过工厂方法或初始方法创建具有某些数值的实例。据我所知,除非你将0视为一种空的NSNumber,否则不可能得到一个“空”的NSNumber


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接