NSNumber is stored in NSUserDefaults

Something strange has happened.

I saved NSNumber with an unsigned long long value in NSUserDefaults. When I remove it, the meaning has simply changed. It seems that the system thinks the number is long and not unsigned long.

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

what is wrong with the code? Thanks!

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 
+6
source share
4 answers

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

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

It is important that you understand this. In your code that you specify, my NSNumber object "number" is equal to "value", you are not asking if the numerical value stored in my NSNumber object is "number", the numerical value stored in my NSNumber object is "value".

The last line of code you wrote shows that in fact your numerical NSNumber values ​​are actually equal.

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

So, you correctly store and retrieve the values, you should use the comparison method == with NSNumber objects that store numerical values ​​(i.e. intValue == intValue, unsignedLongLongValue == unsignedLongLongValue) and do not compare their object identifier together.

Regarding this line of code

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

This is not strange, this is completely normal, since you told NSLog to print the NSObject 'number' view. I am not 100% sure, but I believe that the NSNumber - ( NSString * ) description function by default returns an unsigned int value for the numerical value it contains. That is why you get a large negative number. You can look at the function NSNumber - (NSString *)descriptionWithLocale:(id)aLocale to print the data more logically for you, or you could use

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

Which will give you the correct answer.

EDIT:

In addition to this, having considered what is happening, it is that when you recall your NSNumber object from UserDefaults, its original type of number is not saved (this information is highlighted in the documentation for NSNumber in the overview section)

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

You can verify this yourself if you register the following after extracting the "number" from the user default values ​​(add this to the end of the code that you have in your question) and 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 ... therefore, why are you having problems with the isEqualToNumber function: how the types of numbers are different. If you're so dead using the iSEqualToNumber: function to compare values, you can implement this to get your value from NSUserDefaults.

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

You can look at how to use the NSNumber compare function: see if the return value is NSOrderedSame, however this will not work to compare unsigned and signed values ​​of the same type, so in your situation, I would use the above as getting data from NSUserDefaults cancels " signature "of your number.

+6
source

At the end of the day, if you want to keep NSNumber in NSUserDefaults, this code works for me even for large integers: 881217446193276338

To save:

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

To restore:

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

Keeping this correct, nothing happens to your code except:

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

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

You need something like:

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

+1
source

Here is a speculative answer:

The NSNumber documentation states that:

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

Thus, for this type, you need to use another internal storage and gives only the correct value when you specifically request a long long unsigned value. The description method, which is called in your NSLog statement, may by default use a different type of view.

And / or there may be some unarchiver quirk that prevents the isEqualToNumber method isEqualToNumber working on the default value. If you make this comparison between two NSNumbers created in the same scope, does it work? The correct value is definitely where your last statement returns true.

0
source

Source: https://habr.com/ru/post/898728/


All Articles