NSNumber 存储在 NSUserDefaults 中

发布于 2024-12-08 13:33:51 字数 1067 浏览 0 评论 0原文

刚刚发生了一些奇怪的事情。

我在 NSUserDefaults 中存储了一个带有 unsigned long long 值的 NSNumber。当我检索它时,值刚刚改变。看来系统认为该数字是 long long 而不是 unsigned long long。

更糟糕的是,当我将从 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

Something weird just happened.

I stored a NSNumber with an unsigned long long value in NSUserDefaults. When I retrieve it, the value just changed. It seems that system thinks the number is long long instead of unsigned long long.

What's worse is that when I compare the number retrieved from UserDefaults with the original number, the result is NotEqual!

what's wrong with the code? Thank you!

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

难理解 2024-12-15 13:33:51

进一步回答你的问题。如果您查看文档对于 NSNumber 的 isEqualToNumber: 函数,您会注意到以下行,

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

了解这一点很重要。在您的代码中,您询问的是我的 NSNumber 对象“number”等于“value”,您没有询问我的 NSNumber 对象“number”中存储的数值是否等于我的 NSNumber 对象“value”中存储的数值。

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

NSLog(@"%d", [number unsignedLongLongValue] == value); //1

因此,您正确存储和检索值,您应该对存储数值的 NSNumber 对象使用 == 比较方法(即 intValue == intValue、unsignedLongLongValue == unsignedLongLongValue),而不是比较它们的对象 id。

至于这行代码

NSLog(@"Current Number:%@", number); // -2538698204676668398, weird

这并不奇怪,这是完全正常的,因为您已经告诉 NSLog 打印出“数字”的 NSObject 表示形式。我不是 100% 确定,但我相信 NSNumber 的 - ( NSString * ) description 函数默认返回其包含的数值的 unsigned int 值。这就是为什么您会得到返回的大负数。您可能需要查看 NSNumber 的 - (NSString *)descriptionWithLocale:(id)aLocale 函数来以更符合逻辑的方式打印出数据,或者您可以使用

NSLog(@"Current Number:%llu", [number unsignedLongLongValue]);

Which 会给您正确的答案。

编辑:

除此之外,在调查问题之后,发生的情况是,从 UserDefaults 重新收集 NSNumber 对象时,它的原始数字类型没有被保留(此信息在 NSNumber 的文档中突出显示)概述部分)

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

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

NSLog(@"%s", [number objCType]); //This will log out q
NSLog(@"%s", [[NSNumber numberWithUnsignedLongLong:value] objCType]); //this will log out Q

Q 和 q 之间的区别在于 Q 是一个无符号值...这就是为什么您会遇到 isEqualToNumber: 函数的问题,因为数字类型不同。
如果您执意使用 iSEqualToNumber: 函数来比较值,那么您可以实现它以从 NSUserDefaults 检索您的值。

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

您可以查看使用 NSNumber Compare: 函数来查看返回的值是否为 NSOrderedSame 但这不适用于比较相同类型的无符号与有符号值,因此在您的情况下,我会使用上面的方法从 NSUserDefaults 检索数据剥离您号码的“符号”。

To further answer your question. If you look in the documentation for NSNumber's isEqualToNumber: function you will notice the following line,

Two NSNumber objects are considered equal if they have the same id values or if they have equivalent values

it's important you understand this. In your code you are asking is my NSNumber object "number" equal to "value", you are not asking does the numerical value stored within my NSNumber object "number" equal the numerical value stored within my NSNumber object "value".

The last line of code you have written shows that in fact your NSNumber's numerical values are in fact equal.

NSLog(@"%d", [number unsignedLongLongValue] == value); //1

So you are correctly storing and retrieving the values, you should be using the == comparison method with NSNumber objects stored numerical values (ie intValue == intValue, unsignedLongLongValue == unsignedLongLongValue) and not comparing their object id's together.

As for this line of code

NSLog(@"Current Number:%@", number); // -2538698204676668398, weird

This is not weird, this is perfectly normal, as you have told NSLog to print out an NSObject representation of 'number'. I'm not 100% certain but I believe that NSNumber's - ( NSString * ) description function defaults to return an unsigned int value for the numerical value it contains. This is why you are getting the large negative number returned. You may want to look at NSNumber's - (NSString *)descriptionWithLocale:(id)aLocale function to print out the data in a more logical for for you, or you could use

NSLog(@"Current Number:%llu", [number unsignedLongLongValue]);

Which will give you the right answer.

EDIT:

Further to this, after looking into the issue what is happening is that on recollection of your NSNumber object from UserDefaults it's original number type is not being preserved (this information is highlighted in the documentation for NSNumber in the overview section)

(Note that number objects do not necessarily preserve the type they are created with.)

You can see this yourself if you log the following after retrieving "number" from user defaults (add this to the end of the code you have in your question) and have a look at the encoding values shown here

NSLog(@"%s", [number objCType]); //This will log out q
NSLog(@"%s", [[NSNumber numberWithUnsignedLongLong:value] objCType]); //this will log out Q

The difference between Q and q is that Q is an unsigned value... hence why you are having issues with the isEqualToNumber: function as the number types are different.
If you are so dead set on using the iSEqualToNumber: function to compare values then you could implement this to retrieve your value from NSUserDefaults.

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

You could look at using the NSNumber compare: function to see if the returned value is NSOrderedSame however this will not work for comparing unsigned vs signed values of the same type so in your situation I'd use the above as retrieving the data from NSUserDefaults is stripping the "signedness" of your number.

深居我梦 2024-12-15 13:33:51

在一天结束时,如果您想将 NSNumber 存储到 NSUserDefaults 中,即使对于大整数,此代码也适用于我,例如: 881217446193276338

保存:

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

恢复:

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

At the end of the day if you want to store NSNumber into NSUserDefaults this code works for me even for large integers like: 881217446193276338

To save:

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

To recover:

self.myUser.sessionid = (NSNumber *)[[NSUserDefaults standardUserDefaults] objectForKey:@"sessionid"];
金兰素衣 2024-12-15 13:33:51

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

NSLog(@"Current Number:%@", number);

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

你需要的是这样的东西:

NSLog(@"Current Number:%@", [number stringValue]);

It's storing it correctly, nothing is wrong with your code except:

NSLog(@"Current Number:%@", number);

Here number is a non-string object, you might think of it as a wrapper for a numerical primitive. Or you might think that NSNumber instances objectify a primitive type.

What you need is some thing like:

NSLog(@"Current Number:%@", [number stringValue]);

我不咬妳我踢妳 2024-12-15 13:33:51

这是一个推测性的答案:

NSNumber 文档指出:

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

因此,它必须为此类型使用不同的内部存储,并且仅当您专门要求 unsigned long long 值时才为您提供正确的值。在 NSLog 语句中调用的 description 方法可能默认为不同的表示类型。

和/或解档程序可能存在一些怪癖,导致 isEqualToNumber 方法无法处理从默认值返回的值。如果您对同一范围内创建的两个 NSNumber 进行比较,它有效吗?鉴于您的最后一条语句返回 true,正确的值肯定在某处。

Here is a speculative answer:

The NSNumber documentation states that:

(Note that number objects do not necessarily preserve the type they are created with.) .

So it must be using a different internal storage for this type, and only gives you the correct value when you specifically ask for the unsigned long long value. The description method, which is called in your NSLog statement, may be defaulting to a different representation type.

And / or there may be some quirk of the unarchiver that is preventing the isEqualToNumber method working on the value returned from defaults. If you do that comparison between two NSNumbers created in the same scope, does it work? The correct value is definitely in there somewhere given your last statement returns true.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文