Iphone NSNumber存储在NSUserDefault中

Iphone NSNumber存储在NSUserDefault中,iphone,ios,nsuserdefaults,nsnumber,Iphone,Ios,Nsuserdefaults,Nsnumber,奇怪的事情发生了 我在NSUserDefaults中存储了一个带有无符号long-long值的NSNumber。当我检索它时,值刚刚更改。似乎系统认为数字是长的,而不是无符号的长 更糟糕的是,当我将从UserDefaults检索到的数字与原始数字进行比较时,结果是NotEqual 代码怎么了?谢谢大家! static NSString * const NumberKey = @"MyNumber"; unsigned long long value = 15908045869032883218U

奇怪的事情发生了

我在NSUserDefaults中存储了一个带有无符号long-long值的NSNumber。当我检索它时,值刚刚更改。似乎系统认为数字是长的,而不是无符号的长

更糟糕的是,当我将从UserDefaults检索到的数字与原始数字进行比较时,结果是NotEqual

代码怎么了?谢谢大家!

static NSString * const NumberKey = @"MyNumber";
unsigned long long value = 15908045869032883218ULL;
if ([[NSUserDefaults standardUserDefaults] objectForKey:NumberKey] == nil) {

    NSNumber *number = [NSNumber numberWithUnsignedLongLong:value];
    [[NSUserDefaults standardUserDefaults] setObject:number forKey:NumberKey];
    NSLog(@"Original Number:%@", number); // 15908045869032883218, right
}

NSNumber *number = [[NSUserDefaults standardUserDefaults] objectForKey:NumberKey];
NSLog(@"Current Number:%@", number); // -2538698204676668398, weird
NSLog(@"Current Value:%llu", [number unsignedLongLongValue]); // 15908045869032883218, right
NSLog(@"%d", [number isEqualToNumber:[NSNumber numberWithUnsignedLongLong:value]]); // 0
NSLog(@"%d", [number unsignedLongLongValue] == value); // 1

它正确地存储了它,代码没有任何问题,除了:

NSLog(@“当前编号:%@”,编号)

这里的
number
是一个非字符串对象,您可以将其视为数字原语的包装器。或者您可能认为,
NSNumber
实例对象化了一个基元类型

你需要的是这样的东西:


NSLog(@“当前编号:%@,[Number stringValue])

这里是一个推测性的答案:

NSNumber文件规定:

(请注意,数字对象不一定保留创建它们时使用的类型。)

因此,对于这种类型,它必须使用不同的内部存储,并且只有在您特别要求无符号long值时,它才会提供正确的值。NSLog语句中调用的
description
方法可能默认为不同的表示类型


和/或unarchiver可能有一些怪癖,阻止了
isEqualToNumber
方法处理从默认值返回的值。如果在同一范围内创建的两个NSN编号之间进行比较,是否有效?如果您的上一条语句返回true,那么正确的值肯定在那里的某个地方

进一步回答您的问题。如果查看的是'isEqualToNumber:函数,您将注意到以下行

如果两个NSNumber对象具有相同的id值或具有等效值,则认为它们相等

你明白这一点很重要。在您的代码中,您询问的是我的NSNumber对象“number”是否等于“value”,而不是询问存储在我的NSNumber对象“number”中的数值是否等于存储在我的NSNumber对象“value”中的数值

您编写的最后一行代码表明,实际上NSNumber的数值实际上是相等的

NSLog(@"%d", [number unsignedLongLongValue] == value); //1
因此,在正确存储和检索值时,应该使用==比较方法对NSNumber对象存储的数值(即intValue==intValue,unsignedLongValue==unsignedLongValue)进行比较,而不是将它们的对象id一起进行比较

至于这行代码

NSLog(@"Current Number:%@", number); // -2538698204676668398, weird
这并不奇怪,这是完全正常的,因为您已经告诉NSLog打印出“number”的NSObject表示。我不是100%确定,但我相信NSNumber的
-(NSString*)description
函数默认为它包含的数值返回一个无符号的int值。这就是返回大负数的原因。您可能希望查看NSNumber的
-(NSString*)descriptionWithLocale:(id)aLocale
函数,以更符合逻辑的方式打印数据,或者您可以使用

NSLog(@"Current Number:%llu", [number unsignedLongLongValue]);
这会给你正确的答案

编辑:

除此之外,在研究了这个问题之后,所发生的情况是,从UserDefaults中回忆NSNumber对象时,它的原始编号类型没有被保留(此信息在概述部分的NSNumber文档中突出显示)

(请注意,数字对象不一定保留创建它们时使用的类型。)

如果您在从用户默认值中检索“number”(将其添加到问题中的代码末尾)并查看显示的编码值后记录以下内容,您可以自己看到这一点

Q和Q之间的区别是Q是一个无符号值。。。因此,由于数字类型不同,因此isEqualToNumber:函数存在问题。 如果您死心塌地地使用iSEqualToNumber:函数来比较值,那么您可以实现它来从NSUserDefaults检索您的值

NSNumber *number = [NSNumber numberWithUnsignedLongLong:[[NSUserDefaults standardUserDefaults] objectForKey:NumberKey] unsignedLongLongValue]];

您可以使用NSNumber compare:函数查看返回值是否为nsOrderedName,但是这不适用于比较相同类型的无符号值和有符号值,因此在您的情况下,我将使用上述函数,因为从NSUserDefaults检索数据是在剥离“有符号性”您的号码。

如果您想将NSNumber存储到NSUserDefaults中,那么在一天结束时,此代码对我来说甚至适用于大整数,如:881217446193276338

要保存:

[[NSUserDefaults standardUserDefaults] setObject:self.myUser.sessionid forKey:@"sessionid"];
[[NSUserDefaults standardUserDefaults] synchronize];
要恢复:

self.myUser.sessionid = (NSNumber *)[[NSUserDefaults standardUserDefaults] objectForKey:@"sessionid"];

我想知道的是,为什么从NSUserDefaults检索到的数字不等于原始数字。我同意你的看法。但是,“如果两个NSNumber对象具有相同的id值或具有相同的值,则认为它们相等”意味着我可以依赖isEqualToNumber:来确定两个NSNumber对象的值,但我的代码结果表明我不能依赖此方法。我必须自己定义该值类型,并使用“XXValue”方法和“==”来比较两个NSNumber对象,这并不方便,因为isEqual:method用于许多其他对象,如NSArray、NSDictionary。@Frog在稍微深入地研究之后,您的问题的性质也与下面的答案有关。我将编辑我的原始答案以证明这一点。我很高兴我写下了这个答案,因为6个月后我遇到了同样的问题,我用谷歌搜索了它并登陆这里,看到了我自己的答案,它再次解决了我的问题:)
self.myUser.sessionid = (NSNumber *)[[NSUserDefaults standardUserDefaults] objectForKey:@"sessionid"];