Qual é a melhor maneira de armazenar um NSDate no NSUserDefaults?

174

Existem duas maneiras de armazenar um NSDate nos NSUserDefaults que eu encontrei.

Opção 1 - setObject: forKey:

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

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

Opção 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];

Prós e contras

Opção 1

Isso parece ser compacto e lógico. No entanto, eu tenho preocupações sobre isso dar errado por causa dos erros do Date Formatter .

opção 2

Isso parece ser desajeitado. Também não tenho certeza da precisão disso - em um teste que fiz, quando recuperei a data em que ela estava vencida em 48 segundos, apesar dos documentos da Apple dizerem que o NSTimeInterval tem "precisão de subsegundo".

Exigências

Qualquer que seja o método que eu escolher, deve ser:

  1. Preciso em um segundo.

  2. Legível e confiável.

Minha pergunta

A imprecisão com a opção 2 é porque estou fazendo algo errado?

Qual dessas duas opções você usaria?

Existe outra opção que eu não conheço?

Obrigado!

John Gallagher
fonte

Respostas:

380

Você está desnecessariamente complicando as coisas. Por que você está convertendo a data em um intervalo de tempo (depois o intervalo de tempo em um primitivo diferente)? Apenas [sharedDefaults setObject:theDate forKey:@"theDateKey"]e termine com isso. O NSDate é um dos "principais tipos" suportados pelo formato PLIST (datas, números, seqüências de caracteres, dados, dicionários e matrizes), para que você possa armazená-lo diretamente.

Veja a documentação para prova.

Apenas armazene e recupere a data diretamente e assista à ação certa (incluindo fusos horários, precisão, etc.). Não há formatador envolvido, como outros já disseram.

Joshua Nozzi
fonte
8
Joshua, obrigado pela sua resposta. A razão pela qual tentei a abordagem de bóia complicada e tola foi simplesmente porque eu tinha visto outro grande desenvolvedor fazer isso e pensei que ele tivesse feito isso por algum bom motivo. Obviamente não. Eu deveria ter mais confiança em meu próprio instinto, que era usar setObject: forKey: e ter feito isso.
John Gallagher
7
Sou totalmente violento em relação ao próprio pensamento de complicação desnecessária - uma boa característica para desenvolvedores e pessoas geralmente preguiçosas. :-)
Joshua Nozzi
Os casos em que essa abordagem é usada é principalmente quando NSUserDefaults é usado como uma implementação de armazenamento de preferências do usuário para um middleware genérico ...
Coyote
@ Coiote: Nesse caso, ele ainda está sendo acessado através do NSUserDefaults ou analisado a partir de um arquivo de lista de propriedades, portanto, o mesmo acesso ou análise deve gerar um objeto NSDate adequado, que pode ser convertido conforme necessário.
21911 Joshua Nozzi
3
@JohnGallagher [barra lateral] Não confunda a implementação específica de alguém por uma ruim. Seu sentimento original "pensava que ele havia feito isso por algum bom motivo ... Obviamente não" só poderia ser válido se você entendesse todo o escopo dos requisitos do desenvolvedor. É presunçoso e perspicaz rotular cegamente sua abordagem como "obviamente nenhuma boa razão para fazê-lo dessa maneira". Dito isto, sim, eu concordo com a abordagem de Joshua para simplificar, se você não tiver motivos para fazer o contrário.
Dooleyo
14

Para a opção 1, não acredito que esteja envolvido um formatador de datas. Possivelmente sob o capô, mas imagino que não esteja quebrado. A data é armazenada no formato ISO 8601 .

Para a opção 2, use -setDouble:forKey:e em -doubleForKeyvez das floatversões baseadas em. Essa pode ser a causa dos seus erros de precisão.

John Calsbeek
fonte
Uau, John. Obrigado pela sua resposta muito rápida. Qual você usaria pessoalmente?
John Gallagher
8
Use a data diretamente, não o intervalo de tempo. Duvido que uma API tão básica seja quebrada quando tantos aplicativos dependem dela.
John Calsbeek
Excelente. Esse foi o meu instinto, mas eu tinha visto código de terceiros por um desenvolvedor respeitado que usava o método float, então pensei que haveria uma boa razão para ele ter usado. Obviamente não. Mais uma vez obrigado pela sua resposta!
John Gallagher
1
É possível que o código que você viu não tenha se lembrado de quais tipos podem ser armazenados diretamente em uma lista de propriedades.
John Calsbeek
2
Para o registro - flutuadores de 32 bits têm apenas 24 bits de precisão, então 1970 é agora de 40 anos, que é 40 * 365 * 86400 segundos e (40 * 365 * 86 400) / (2 ** 24) = 75segundo erro . A precisão dupla é o intervalo DateTime, e sua precisão RAW agora é melhor que um milionésimo de segundo.
Tom Andersen
5

Use NSUserDefaults; As datas são armazenadas no horário Zulu, portanto, não há problemas de fuso horário com que se preocupar. Armazene-o no seu fuso horário, retire-o em outro fuso horário, você ficará bem, o sistema lida com a conversão (nenhum formatador de data para se preocupar).

Ben Gottlieb
fonte
0

Se você estiver salvando a data de validade da API do Facebook Graph, eu usaria a * Opção 2 * .

A opção dois pode ser facilmente convertida em uma string (usando stringWithFormat). Mais importante, ele funciona para a API do Graph.

Além disso, você não precisa se preocupar com o formato da sua data. Não lidar com NSDateFormatter vale a possibilidade de um erro de 48 segundos.

Nate Symer
fonte