NSNumber, сохраненный в NSUserDefaults

#iphone #ios #nsuserdefaults #nsnumber

#iPhone #iOS #nsuserdefaults #nsnumber

Вопрос:

Только что произошло что-то странное.

Я сохранил NSNumber с длинным значением long long без знака в NSUserDefaults. Когда я извлекаю его, значение просто изменилось. Кажется, что система считает, что число длинное, а не длинное без знака.

Хуже всего то, что когда я сравниваю число, полученное из UserDefaults, с исходным номером, результат не равен!

что не так с кодом? Спасибо!

 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
  

Ответ №1:

Для дальнейшего ответа на ваш вопрос. Если вы посмотрите в документации для функции isEqualToNumber: NSNumber, вы заметите следующую строку,

Два объекта NSNumber считаются равными, если они имеют одинаковые значения id или если они имеют эквивалентные значения

важно, чтобы вы это понимали. В вашем коде, который вы спрашиваете, равен ли «значение» моего объекта NSNumber «number», вы не спрашиваете, равно ли числовое значение, хранящееся в моем объекте NSNumber «number», числовому значению, хранящемуся в моем объекте NSNumber «value».

Последняя строка написанного вами кода показывает, что на самом деле числовые значения вашего NSNumber фактически равны.

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

Чтобы вы правильно сохраняли и извлекали значения, вы должны использовать метод сравнения == с объектами NSNumber, хранящими числовые значения (т.Е. intValue == intValue, unsignedLongLongValue == unsignedLongLongValue ), а не сравнивать их идентификаторы объектов вместе.

Что касается этой строки кода

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

Это не странно, это совершенно нормально, поскольку вы сказали NSLog распечатать представление NSObject ‘number’. Я не уверен на 100%, но я считаю, что - ( NSString * ) description функция NSNumber по умолчанию возвращает значение int без знака для содержащегося в нем числового значения. Вот почему вы возвращаете большое отрицательное число. Возможно, вы захотите взглянуть на - (NSString *)descriptionWithLocale:(id)aLocale функцию NSNumber, чтобы распечатать данные более логичным для вас способом, или вы могли бы использовать

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

Который даст вам правильный ответ.

Редактировать:

В дополнение к этому, после изучения проблемы происходит то, что при запоминании вашего объекта NSNumber из UserDefaults его исходный тип номера не сохраняется (эта информация выделена в документации для NSNumber в разделе обзор)

(Обратите внимание, что объекты number не обязательно сохраняют тип, с которым они созданы.)

Вы можете убедиться в этом сами, если после получения «number» из пользовательских настроек по умолчанию вы зарегистрируете следующее (добавьте это в конец кода, который у вас есть в вашем вопросе), и посмотрите на значения кодировки, показанные здесь

 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:, чтобы узнать, является ли возвращаемое значение NSOrderedSame, однако это не сработает для сравнения беззнаковых и подписанных значений одного и того же типа, поэтому в вашей ситуации я бы использовал приведенное выше, поскольку извлечение данных из NSUserDefaults удаляет «подписанность» вашего номера.

Комментарии:

1. Я согласен с вами. Однако «Два объекта NSNumber считаются равными, если они имеют одинаковые значения идентификатора или если они имеют эквивалентные значения» означает, что я могу зависеть от isEqualToNumber: для определения значения двух объектов NSNumber, но результат моего кода показывает, что я не могу зависеть от этого метода. Я должен сам определить тип значения и использовать метод «XXValue» и «==» для сравнения двух объектов NSNumber, что неудобно, потому что isEqual: метод используется во многих других объектах, таких как NSArray, NSDictionary.

2. @Swordsfrog изучив это немного подробнее, характер ваших проблем также связан с ответом ниже. Я отредактирую свой первоначальный ответ, чтобы продемонстрировать это.

Ответ №2:

В конце концов, если вы хотите сохранить NSNumber в NSUserDefaults, этот код работает для меня даже для больших целых чисел, таких как: 881217446193276338

Чтобы сохранить:

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

Для восстановления:

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

Комментарии:

1. Я рад, что написал этот ответ, потому что через 6 месяцев я столкнулся с той же проблемой, я погуглил ее и приземлился здесь и увидел свой собственный ответ, и он снова решил мою проблему 🙂

Ответ №3:

Он сохраняет его правильно, в вашем коде нет ничего плохого, кроме:

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

Вот number нестроковый объект, вы можете рассматривать его как оболочку для числового примитива. Или вы можете подумать, что NSNumber экземпляры объективируют примитивный тип.

Вам нужно что-то вроде:

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

Комментарии:

1. Я хочу знать, почему число, полученное из NSUserDefaults, не равно исходному числу.

Ответ №4:

Вот предположительный ответ:

В документации NSNumber указано, что:

(Обратите внимание, что объекты number не обязательно сохраняют тип, с которым они созданы.) .

Таким образом, он должен использовать другое внутреннее хранилище для этого типа и выдает вам правильное значение только тогда, когда вы специально запрашиваете значение без знака long long . description Метод, который вызывается в вашем операторе NSLog, может по умолчанию использовать другой тип представления.

И / или может быть какая-то причуда разархиватора, которая мешает isEqualToNumber методу работать со значением, возвращаемым по умолчанию. Если вы выполняете это сравнение между двумя NSNumbers, созданными в одной и той же области, работает ли это? Правильное значение определенно где-то там, учитывая, что ваше последнее утверждение возвращает true.