何时(不)滥用NSUserDefaults

24

我想知道以下几点使用NSUserDefaults的指南:
1 - 我可以多久读取一次NSUserDefaults中的数据
2 - 合理存储在NSUserDefaults中的数据量有多大

显然,NSUserDefaults的使用是有限制的,但我很难确定什么是合理的,什么是不合理的。

以下是一些例子:

  • 如果我的游戏有一个选项,让电脑成为玩家之一,那我将使用NSUserDefaults来保存布尔值。这一点是清楚的。但是,在我的游戏中,每当我想知道计算机是否是玩家时,使用NSUserDefaults是合理的呢?还是我应该使用实例变量来代替?假设我需要每秒检查一次这个布尔值。如果是100毫秒呢?如果是10秒呢?

  • 如果我的游戏有50个移动对象,并且我希望它们的位置和速度在用户退出应用程序时被存储,NSUserDefaults是存储这些数据的合理位置吗?如果只有20个移动对象呢?如果是200个呢?

5个回答

17

我想知道以下指南: 1 - 我可以多久从NSUserDefaults中读取一次

您可以相对频繁地进行读取。默认情况下的开销与线程安全的NSDictionary类似。

2 - 我可以在NSUserDefaults中合理存储多少数据

物理上,您可以存储比需要更多的数据。逻辑上,最大值是受限于磁盘空间和所需速度。请记住,在启动/关闭和其他各种时刻,NSUserDefaults的表示形式都要从磁盘中读取或写入。

如果我的游戏有一个选项是让电脑成为其中一个玩家,我将使用NSUserDefaults来保存该布尔值。这点很清楚。但是,每次我想知道计算机是否是玩家时,访问NSUserDefaults也合理吗?还是应该使用实例变量?

只需在对手对象中添加一个const bool。除了内存(不会显著),没有运行时损失。

假设我需要每秒检查一次该布尔值。如果是100毫秒呢?10秒呢?

同样,它就像线程安全的NSDictionary(哈希)。它会相当快,并且在该频率下读取足够快。最佳设计是否取决于程序。如果数据量很大,那么性能将受到影响。

如果我的游戏有50个移动对象,并且我希望在用户退出应用程序时存储它们的位置和速度,NSUserDefaults是否是存储这些数据的合理地方?那么20个移动对象呢?200个呢?

理论上是可以的,但是我建议在游戏过程中不要通过NSUserDefaults进行读写操作;只需在需要保存/加载状态时进行即可。

我不建议将所有东西保存在用户默认设置中。只需为游戏状态创建文件表示,并将用户默认设置用于其设计用途。如果它非常庞大并且经常写入,则实现可能会定期将状态刷新到磁盘上,这可能需要相对较长的时间。


关于你最后一段的内容,每当状态发生变化时,可以将其刷新到磁盘1至5次,或者使用定时器每分钟刷新一次。 - Nate Symer

15

不要担心限制,相反,问自己这个简单的问题:

这是一个偏好吗?

如果它是一个偏好设置,那么应该放在用户默认设置中。这就是用户默认设置的作用。如果不是,那么应该放在文档目录中(或者,在Mac上,可能在应用程序支持中)。

在iOS上,您可以通过是否适合(如果可能)将其放入设置捆绑包以在设置应用程序中进行显示和编辑来确定它是否为首选项。在Mac OS X上,您通常可以通过是否适合将其放入首选项窗口来确定它是否为首选项。

当然,这取决于您的判断。例如,Mac版Stanza就弄错了,将非首选项放入其首选项窗口中。

您还可以通过其反面考虑此问题:

这是用户创建的数据吗?

默认情况下会有默认值的首选项不是由用户创建的数据,而是用户覆盖的数据。虽然失去它并不比其他数据更糟糕,但它告诉您应该将其存储在哪里。


感谢大家的回复,对我帮助很大。 - double07

10
没有人提到的主要性能问题是用户的主目录可能位于网络卷上,速度可能不是特别快。虽然这不是理想情况,但确实会发生,所以如果您担心性能问题,则应该针对此进行测试。
话虽如此,NSUserDefaults使用内存缓存,只在同步时才产生成本。根据文档,同步会“自动…定期间隔”进行;我认为只有在发生更改时才会应用此规则。
因此,在检查计算机是否为播放器的情况下,每秒钟使用NSUserDefaults一次不应该是问题,因为它已经被缓存了。对于存储游戏状态而言,如果您不断更新它,那么这可能会成为性能问题,并且正如Peter Hosey所说,这是一种语义滥用。

7

NSUserDefaults可以存储数百到数千个项目(它基本上只是一个包装器,围绕属性列表序列化构建而成)。就您的应用程序开销而言,最好的方法是尝试使用分析工具。


2
谢谢您提到使用分析器。您是我的英雄。 - Nate Symer

2
NSUserDefaults基本上是从磁盘加载NSDictionary的包装器(同时也将其写入磁盘)。您可以在NSUserDefaults中存储大量数据,但是您无法控制它使用多少内存以及如何从磁盘读取。
我会针对不同的信息/数据使用不同的技术。
- 对于来自服务器、偏好设置、用户信息等小数据,我会使用NSUserDefaults。 - 对于登录信息(访问令牌、敏感数据),我会使用密钥链。密钥链还可以用于应用被删除时不应删除的数据。 - 对于大量服务器数据或游戏数据,我会将其写入磁盘,但保留在内存中。
在您的情况下,我会将其保留在内存中(可能是@property),但我会定期将其写入磁盘(例如每次更改1到5次,使用int ivar)。确保此磁盘写入方法位于AppDelegate中,以便在执行它的视图控制器关闭时不会失败。
这样,数据很容易访问,同时也保存在磁盘上以备不时之需。

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