在NSUserDefaults中存储NSDate的最佳方法是什么?

178

我知道两种将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

该方案看起来紧凑而且合乎逻辑。然而,我担心由于 日期格式化程序错误 会导致它出现问题。

选项2

这个方案看起来很笨拙。我也不确定它的准确性-在我进行的一个测试中,当我取回日期时,它相差了48秒,尽管Apple文档说NSTimeInterval具有“亚秒精度”。

要求

无论我选择哪种方法,它都必须:

  1. 精确到秒。

  2. 易读且可靠。

我的问题

选项2的不准确是因为我做错了什么吗?

在这两个选项中,您会选择哪个?

还有其他我不知道的选项吗?

谢谢!

4个回答

386

你正在不必要地复杂化事情。为什么要将日期转换为时间间隔(然后再转换为不同的原始数据类型)?只需使用[sharedDefaults setObject:theDate forKey:@"theDateKey"] 就可以了。NSDate是PLIST格式支持的“主要类型”之一(包括日期、数字、字符串、数据、字典和数组),因此您可以直接将其存储。

请参阅文档以获得证明。

只需直接存储和检索日期,观察它正确执行操作(包括时区、精度等)。正如其他人所说,这里没有涉及到格式化器。


8
Joshua,谢谢你的回答。我之所以尝试这种愚蠢的复杂浮点数方法,仅仅是因为我见过另一个很棒的开发人员这样做,而我认为他肯定有充分的理由。显然不是这样的。我应该更有信心相信自己的直觉,使用setObject:forKey:就行了。 - John Gallagher
7
我对不必要的复杂性感到极度愤怒,这是开发人员和通常懒惰的人的好品质。 :-) - Joshua Nozzi
没错。但如果你正在使用Cocoa Bindings(NSWhateverController),请确保使用NSUserDefaultsController,而不是NSUserDefaults。 - Joshua Nozzi
3
不要将某人的具体实现误认为是一种不好的方式。你最初的想法“他认为这么做有好的原因...显然没有”,只有当你了解该开发人员所有需求的整个范围时,才能成立。盲目地将他的方法标记为“显然没有充分的理由来这样做”,这是傲慢和心胸狭窄的表现。话虽如此,如果你没有其他理由,我同意Joshua的方法是保持简单的方式。 - dooleyo
在这种情况下,我认为可以毫不夸张地说,在日期的情况下将其简化为仅表示从某个任意时期开始的秒数的无符号整数是明显的糟糕想法(这可能会改变,从而抵消所表示的日期)。特别是当考虑到时区、夏令时和其他本地调整时。即使考虑跨平台参数,也会使这种方法失效,因为存在不同的时期(不是行业标准)。我敢于断言,最初的开发人员做错了。 - Joshua Nozzi
显示剩余3条评论

14

对于选项#1,我不认为涉及日期格式化程序。可能在底层实现中使用了,但我想它没有出错。日期以ISO 8601格式存储。

对于选项#2,请使用-setDouble:forKey:-doubleForKey代替基于float的版本。这可能是您精度错误的原因。


哇,John。谢谢你的非常迅速的回答。你个人会使用哪一个? - John Gallagher
8
请直接使用日期,而不是时间间隔。我怀疑这样一个基础的API会出现问题,因为许多应用程序都依赖它。 - John Calsbeek
1
有可能你看到的那段代码是开发人员忘记了哪些类型可以直接存储在属性列表中。 - John Calsbeek
2
记录一下 - 32位浮点数仅具有24位的精度,因此从1970年到现在是40年,即40 * 365 * 86400秒,而 (40 * 365 * 86 400) / (2 ** 24) = 75 秒误差。双精度是 DateTime间隔所需要的,其原始精度现在已经达到了百万分之一秒。 - Tom Andersen
由于某些原因,有些人总是使用“float”而不是“double”,然后他们会遇到像这样的错误。完全没有必要。规则:除非您能给出非常好的和令人信服的理由表明“float”更好,否则请使用“double”。 - gnasher729
显示剩余6条评论

5
使用NSUserDefaults;日期以Zulu时间存储,因此无需担心时区问题。将其存储在您的时区中,在另一个时区中提取它,您会发现一切都很好,系统会处理转换(无需担心日期格式化程序)。

1
如果您正在保存来自Facebook Graph API的到期日期,我建议使用*选项2*
选项二可以轻松地转换为字符串(使用stringWithFormat)。最重要的是,它适用于Graph API。
此外,您不必担心日期格式。不需要处理NSDateFormatter值得冒48秒错误的风险。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接