#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.