Jaki jest optymalny sposób przechowywania NSDate w NSUserDefaults?

174

Istnieją dwa sposoby przechowywania NSDate w NSUserDefaults, z którymi się spotkałem.

Opcja 1 - setObject: forKey:

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

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

Opcja 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];

Plusy i minusy

opcja 1

Wydaje się, że jest to zwięzłe i logiczne. Jednak martwię się, że to pójdzie nie tak z powodu błędów programu Date Formatter .

Opcja 2

To wydaje się niezdarne. Nie jestem też pewien dokładności tego - w jednym z testów, które przeprowadziłem, kiedy odzyskałem datę wstecz, minęło 48 sekund, mimo że w dokumentach Apple Docs stwierdzono, że NSTimeInterval ma „dokładność podsekundową”.

Wymagania

Niezależnie od wybranej metody, musi to być:

  1. Precyzja w ciągu sekundy.

  2. Czytelne i niezawodne.

Moje pytanie

Czy jest to niedokładność w Opcji 2, ponieważ robię coś źle?

Której z tych dwóch opcji byś użył?

Czy jest inna opcja, której nie jestem świadomy?

Dzięki!

John Gallagher
źródło

Odpowiedzi:

380

Niepotrzebnie komplikujesz sprawy. Dlaczego konwertujesz datę na przedział czasu (a następnie przedział czasu na inny prymityw)? Po prostu [sharedDefaults setObject:theDate forKey:@"theDateKey"]skończ z tym. NSDate to jeden z „głównych typów” obsługiwanych przez format PLIST (daty, liczby, ciągi znaków, dane, słowniki i tablice), więc możesz je po prostu przechowywać bezpośrednio.

Zobacz dokumentację, aby uzyskać dowód.

Po prostu zapisz i pobierz datę bezpośrednio i zobacz, jak działa prawidłowo (w tym strefy czasowe, precyzja itp.). Jak powiedzieli inni, nie jest zaangażowany żaden formatyzator.

Joshua Nozzi
źródło
8
Joshua, dzięki za odpowiedź. Powodem, dla którego wypróbowałem głupią, zawiłą metodę float, było po prostu to, że widziałem, jak robi to inny świetny programista i pomyślałem, że zrobił to z jakiegoś dobrego powodu. Oczywiście, że nie. Powinienem mieć więcej zaufania do własnego instynktu, jakim było użycie setObject: forKey: i skończyłem z tym.
John Gallagher,
7
Jestem wręcz gwałtowny wobec samej myśli o niepotrzebnych komplikacjach - dobra cecha programistów i ogólnie leniwych ludzi. :-)
Joshua Nozzi
Przypadki, w których takie podejście jest stosowane, to głównie przypadki, gdy NSUserDefaults jest używane jako magazynowa implementacja preferencji użytkownika dla ogólnego oprogramowania pośredniego ...
Coyote,
@Coyote: W takim przypadku dostęp do niego jest nadal uzyskiwany przez NSUserDefaults lub analizowany z pliku listy właściwości, więc ten sam dostęp lub analiza powinna dać odpowiedni obiekt NSDate, który można przekonwertować w razie potrzeby.
Joshua Nozzi
3
@JohnGallagher [pasek boczny] Nie pomyl czyjejś konkretnej implementacji z kiepską. Twój pierwotny sentyment „myślał, że zrobił to z jakiegoś dobrego powodu… Oczywiście nie” może być ważny tylko wtedy, gdy zrozumiesz cały zakres wymagań wspomnianego twórcy. Ślepe określanie swojego podejścia jako „oczywiście nie ma dobrego powodu, aby robić to w ten sposób” jest arogancją i ograniczeniem umysłu. Biorąc to pod uwagę, tak, zgodziłbym się z podejściem Joshuy, aby było to proste, jeśli nie masz powodu, aby postępować inaczej.
dooleyo,
14

W przypadku opcji nr 1 nie sądzę, aby w grę wchodził program formatujący daty. Prawdopodobnie pod maską, ale wyobrażam sobie, że nie jest zepsuty. Data jest przechowywana w formacie ISO 8601 .

W przypadku opcji nr 2 użyj -setDouble:forKey:i -doubleForKeyzamiast floatwersji bazowych. To może być przyczyną twoich błędów precyzji.

John Calsbeek
źródło
Wow, John. Dziękuję za bardzo szybką odpowiedź. Którego osobiście użyłbyś?
John Gallagher,
8
Użyj bezpośrednio daty, a nie przedziału czasu. Wątpię, aby taki podstawowy interfejs API był zepsuty, gdy tak wiele aplikacji na nim polega.
John Calsbeek,
Doskonały. Taki był mój instynkt, ale widziałem kod strony trzeciej autorstwa szanowanego programisty, który używał metody float, więc pomyślałem, że byłby jakiś dobry powód, dla którego go użył. Oczywiście, że nie. Jeszcze raz dziękuję za odpowiedź!
John Gallagher,
1
Możliwe, że ten kod, który widziałeś, był programistą, który nie pamiętał, które typy mogą być przechowywane bezpośrednio na liście właściwości.
John Calsbeek,
2
Dla przypomnienia - 32-bitowe liczby zmiennoprzecinkowe mają tylko 24 bity dla dokładności, więc 1970 do teraz to 40 lat, czyli 40 * 365 * 86400 sekund, a (40 * 365 * 86 400) / (2 ** 24) = 75 sekund błąd . Podwójna precyzja jest tym, czym jest interwał DateTime, a jego precyzja RAW jest teraz lepsza niż milionowa część sekundy.
Tom Andersen
5

Użyj NSUserDefaults; daty są przechowywane w czasie Zulu, więc nie musisz się martwić o strefę czasową. Przechowuj go w swojej strefie czasowej, wyciągnij w innej strefie czasowej, wszystko będzie dobrze, system zajmie się konwersją (nie musisz się martwić o formatowanie daty).

Ben Gottlieb
źródło
0

Jeśli zapisujesz datę wygaśnięcia z Facebook Graph API, użyłbym * Opcji 2 * .

Opcję drugą można łatwo przekonwertować na ciąg (za pomocą stringWithFormat). Co najważniejsze, działa w przypadku interfejsu Graph API.

Nie musisz też martwić się o format daty. Brak obsługi NSDateFormatter jest wart możliwości wystąpienia 48-sekundowego błędu.

Nate Symer
źródło