Каков оптимальный способ хранения NSDate в NSUserDefaults?

174

Есть два способа хранения NSDate в NSUserDefaults, с которыми я столкнулся.

Вариант 1 - setObject: forKey:

// Set
NSDate *myDate = [NSDate date];
[[NSUserDefaults standardUserDefaults] setObject:myDate forKey:@"myDateKey"];

// Get
NSDate *myDate = (NSDate *)[[NSUserDefaults standardUserDefaults] objectForKey:@"myDateKey"];

Вариант 2 - timeIntervalSince1970

// Set
NSDate *myDate = [NSDate date];
NSTimeInterval myDateTimeInterval = [myDate timeIntervalSince1970];
[[NSUserDefaults standardUserDefaults] setFloat:myDateTimeInterval forKey:@"myDateKey"];

// Get
NSTimeInterval myDateTimeInterval = [[NSUserDefaults standardUserDefaults] floatForKey:@"myDateKey"];
NSDate *myDate = [NSDate dateWithTimeIntervalSince1970:myDateTimeInterval];

Плюсы и минусы

Опция 1

Это кажется компактным и логичным. Тем не менее, у меня есть опасения по поводу этого происходит неправильно из - за Дата Formatter ошибок .

Вариант 2

Это кажется неуклюжим. Я также не уверен в ее точности - в одном из тестов, которые я проводил, когда я возвращал дату, она истекла на 48 секунд, несмотря на то, что в Apple Docs сказали, что NSTimeInterval обладает «точностью до секунды».

Требования

Какой бы метод я ни выбрал, он должен быть:

  1. Точно с точностью до секунды.

  2. Читаемый и надежный.

Мой вопрос

Является ли неточность с вариантом 2, потому что я делаю что-то не так?

Какой из этих двух вариантов вы бы использовали?

Есть ли другой вариант, о котором я не знаю?

Спасибо!

Джон Галлахер
источник

Ответы:

380

Вы без необходимости усложняете вещи. Почему вы конвертируете дату во временной интервал (затем временной интервал в другой примитив)? Просто [sharedDefaults setObject:theDate forKey:@"theDateKey"]и покончим с этим. NSDate - это один из «основных типов», поддерживаемых форматом PLIST (даты, числа, строки, данные, словари и массивы), поэтому вы можете просто сохранить его напрямую.

Смотрите документацию для доказательства.

Просто сохраняйте и извлекайте дату напрямую и смотрите, как она работает (включая часовые пояса, точность и т. Д.). Как говорят другие, в этом нет никакого форматера.

Джошуа Ноцци
источник
8
Джошуа, спасибо за твой ответ. Причина, по которой я попробовал глупый замысловатый подход, заключалась в том, что я просто видел, как другой великий разработчик делал это, и я думал, что он сделал это по какой-то веской причине. Очевидно нет. У меня должно быть больше уверенности в моем собственном инстинкте, который должен был использовать setObject: forKey: и покончил с этим.
Джон Галлахер
7
Я совершенно жесток по отношению к самой мысли о ненужных осложнениях - хорошая черта для разработчиков и вообще ленивых людей. :-)
Джошуа Ноцци
В большинстве случаев такой подход используется, когда NSUserDefaults используется как реализация хранилища пользовательских настроек для универсального промежуточного программного обеспечения ...
Coyote
@Coyote: В этом случае к нему все еще обращаются через NSUserDefaults или анализируют из файла списка свойств, поэтому тот же самый доступ или синтаксический анализ должны привести к правильному объекту NSDate, который может быть преобразован по мере необходимости.
Джошуа Ноцци
3
@JohnGallagher [боковая панель] Не путайте чью-то конкретную реализацию с плохой. Ваше первоначальное мнение «он думал, что сделал это по какой-то веской причине ... Очевидно, нет» может быть верным, только если вы понимаете весь объем требований указанного разработчика. Самонадеянно и недальновидно слепо маркировать его подход как «явно не веский повод сделать это таким образом». При этом да, я бы согласился с подходом Джошуа, чтобы он был простым, если у вас нет причин поступать иначе.
Доулио
14

Для варианта № 1 я не верю, что используется форматер даты. Возможно, под капотом, но я думаю, что он не сломан. Дата хранится в форме ISO 8601 .

Для варианта № 2 используйте -setDouble:forKey:и -doubleForKeyвместо floatверсий на основе. Это может быть причиной ваших ошибок точности.

Джон Калсбек
источник
Вау, Джон. Спасибо за ваш очень быстрый ответ. Что бы вы лично использовали?
Джон Галлахер
8
Используйте дату напрямую, а не интервал времени. Я сомневаюсь, что такой базовый API нарушается, когда на него полагаются многие приложения.
Джон Калсбек
Превосходно. Это был мой инстинкт, но я видел сторонний код от уважаемого разработчика, который использовал метод float, поэтому я подумал, что будет веская причина, по которой он его использовал. Очевидно нет. Еще раз спасибо за ваш ответ!
Джон Галлахер
1
Возможно, что тот код, который вы видели, был разработчиком, который не помнил, какие типы могут быть сохранены непосредственно в списке свойств.
Джон Калсбек
2
Для записи - 32-разрядные числа с плавающей запятой имеют только 24 бита для точности, поэтому с 1970 года по настоящее время 40 лет, что составляет 40 * 365 * 86400 секунд, и (40 * 365 * 86 400) / (2 ** 24) = ошибка 75 секунд , Двойная точность - это то, что интервал DateTime, и его точность RAW теперь лучше, чем миллионная доля секунды.
Том Андерсен
5

Используйте NSUserDefaults; даты хранятся в зулусском времени, поэтому нет проблем с часовыми поясами. Сохраните его в своем часовом поясе, вытащите его в другом часовом поясе, все будет в порядке, система выполняет преобразование (не нужно беспокоиться о форматере даты).

Бен Готлиб
источник
0

Если вы сохраняете дату истечения срока действия из API Graph Facebook, я бы использовал * Вариант 2 * .

Второй вариант может быть легко преобразован в строку (используя stringWithFormat). Самое главное, это работает для Graph API.

Кроме того, вам не нужно беспокоиться о формате вашей даты. Отказ от NSDateFormatter стоит вероятности ошибки 48 секунд.

Нейт Симер
источник