Quelle est la manière optimale de stocker un NSDate dans NSUserDefaults?

174

Il y a deux façons de stocker un NSDate dans NSUserDefaults que j'ai rencontrées.

Option 1 - setObject: forKey:

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

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

Option 2 - timeIntervalDepuis 1970

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

Avantages et inconvénients

Option 1

Cela semble être compact et logique. Cependant, je crains que cela ne se passe mal à cause des bogues du formateur de date .

Option 2

Cela semble maladroit. Je ne suis pas non plus sûr de l'exactitude de celui-ci - dans un test que j'ai fait, lorsque j'ai récupéré la date, il était dépassé de 48 secondes, malgré les documents Apple disant que NSTimeInterval a une "précision inférieure à la seconde".

Exigences

Quelle que soit la méthode que je choisis, elle doit être:

  1. Précis à moins d'une seconde.

  2. Lisible et fiable.

Ma question

L'inexactitude de l'option 2 est-elle due au fait que je fais quelque chose de mal?

Laquelle de ces deux options utiliseriez-vous?

Y a-t-il une autre option dont je ne suis pas au courant?

Merci!

John Gallagher
la source

Réponses:

380

Vous compliquez inutilement les choses. Pourquoi convertissez-vous la date en un intervalle de temps (puis l'intervalle de temps en une primitive différente)? Juste [sharedDefaults setObject:theDate forKey:@"theDateKey"]et en finir. NSDate est l'un des "principaux types" pris en charge par le format PLIST (dates, nombres, chaînes, données, dictionnaires et tableaux), vous pouvez donc simplement le stocker directement.

Voir la documentation pour preuve.

Il suffit de stocker et de récupérer la date directement et de la regarder faire la bonne chose (y compris les fuseaux horaires, la précision, etc.). Il n'y a pas de formateur impliqué, comme d'autres l'ont dit.

Joshua Nozzi
la source
8
Joshua, merci pour votre réponse. La raison pour laquelle j'ai essayé l'approche stupide du flotteur alambiqué était simplement parce que j'avais vu un autre grand développeur le faire et je pensais qu'il l'avait fait pour une bonne raison. Évidemment pas. Je devrais avoir plus confiance en mon propre instinct, qui était d'utiliser setObject: forKey: et j'en ai fini avec.
John Gallagher
7
Je suis carrément violent à l'idée même de complication inutile - un bon trait pour les développeurs et les gens généralement paresseux. :-)
Joshua Nozzi
Les cas où une telle approche est utilisée sont principalement lorsque NSUserDefaults est utilisé comme une implémentation de stockage des préférences utilisateur pour un middleware générique ...
Coyote
@Coyote: Dans ce cas, il est toujours accessible via NSUserDefaults ou analysé à partir d'un fichier de liste de propriétés, de sorte que le même accès ou l'analyse devrait donner un objet NSDate approprié, qui peut être converti si nécessaire.
Joshua Nozzi
3
@JohnGallagher [sidebar] Ne confondez pas l'implémentation spécifique de quelqu'un pour une mauvaise. Votre sentiment initial "pensait qu'il l'avait fait pour une bonne raison ... Evidemment non" ne pouvait être valable que si vous compreniez toute la portée des exigences dudit développeur. Il est présomptueux et borné d'étiqueter aveuglément son approche comme «manifestement aucune bonne raison de le faire de cette façon». Cela étant dit, oui, je serais d'accord avec l'approche de Joshua pour garder les choses simples si vous n'avez aucune raison de faire autrement.
dooleyo
14

Pour l'option n ° 1, je ne crois pas qu'un formateur de date soit impliqué. Peut-être sous le capot, mais j'imagine que ce n'est pas cassé. La date est stockée sous la forme ISO 8601 .

Pour l'option n ° 2, utilisez -setDouble:forKey:et à la -doubleForKeyplace des floatversions basées sur. Cela pourrait être la cause de vos erreurs de précision.

John Calsbeek
la source
Wow, John. Merci pour votre réponse très rapide. Lequel utiliseriez-vous personnellement?
John Gallagher
8
Utilisez la date directement, pas l'intervalle de temps. Je doute qu'une API aussi basique soit cassée alors que tant d'applications en dépendent.
John Calsbeek
Excellent. C'était mon instinct, mais j'avais vu du code tiers par un développeur respecté qui utilisait la méthode float, alors j'ai pensé qu'il y aurait une bonne raison pour laquelle il l'avait utilisé. Évidemment pas. Merci encore pour votre réponse!
John Gallagher
1
Il est possible que le code que vous avez vu soit le développeur ne se souvenant pas des types pouvant être stockés directement dans une liste de propriétés.
John Calsbeek
2
Pour mémoire - les flottants 32 bits ont seulement 24 bits pour la précision, donc 1970 à maintenant est de 40 ans, ce qui est de 40 * 365 * 86400 secondes, et (40 * 365 * 86 400) / (2 ** 24) = erreur de 75 secondes . La double précision est ce qu'est un intervalle DateTime, et sa précision RAW est maintenant meilleure qu'un millionième de seconde.
Tom Andersen
5

Utilisez NSUserDefaults; les dates sont stockées à l'heure zoulou, il n'y a donc pas de problèmes de fuseau horaire à s'inquiéter. Stockez-le dans votre fuseau horaire, retirez-le dans un autre fuseau horaire, tout ira bien, le système gère la conversion (pas de formateur de date à craindre).

Ben Gottlieb
la source
0

Si vous enregistrez la date d'expiration de l'API Facebook Graph, j'utiliserais * Option 2 * .

La deuxième option peut être facilement convertie en chaîne (en utilisant stringWithFormat). Plus important encore, cela fonctionne pour l'API Graph.

De plus, vous n'avez pas à vous soucier du format de votre date. Ne pas traiter NSDateFormatter vaut la possibilité d'une erreur de 48 secondes.

Nate Symer
la source