在NSUserDefaults中存储值是否有限制?

91

我在应用程序中使用了NSUserDefaults来存储一些值。在NSUserDefaults中存储值是否有限制?


NSUserDefaults旨在处理相对较小的数据,非常频繁地查询和偶尔修改。以其他方式使用它可能会比适合这些用途的解决方案慢或使用更多内存。目前,本地用户默认设置没有限制,除了tvOS。我通过“cmd click”UserDefaults找到了这些信息,该操作将带您进入其源文件。我查阅了文档以寻找这些信息,但未能在其中找到。 - Bobby
1
我不确定,但你可以查看这个链接 - TheTiger
10个回答

62
只要 iPhone/iPad 上有足够的空间,您就可以存储 NSUserDefaults 值。所有这些值都存储在 .plist 文件中,这个文件非常小,大部分时间都小于 1 kb(除非您存储了大量数据)。

添加大文件/数据后,设备性能是否会受到影响? - iDeveloper
1
可能是真的,但请查看此链接。哪个是正确的? - TheTiger
试着用Xcode打开其中一个plist文件,你会看到漂亮的彩虹旋转器带来的性能损耗。这是Xcode的情况,但设备上可能也会有类似的影响。 - Victor Engel

51

存储的数据类型有限制:必须全部是属性列表对象,包括NSStringNSNumberNSDataNSArrayNSDictionary。此外,如果要存储NSArrayNSDictionary,则其中的值也必须是属性列表对象;而且NSDictionary中所有的键值都必须是字符串。

请注意,像UIColor这样的对象不在上述列表中。因此,如果您想将颜色存储在默认数据库中,您需要先将其转换为字符串或数据对象,然后在读取默认值时再将其转换回来。

至于大小限制,没有文档说明,但请注意所有数据将作为属性列表文件存储。整个文件会被一次性地读入和写出,因此如果您使用NSUserDefaults来存储大量仅在部分更改的数据,那么您将浪费很多时间进行不必要的I/O操作。


6
这个单文件的点非常关键。信息很有用,谢谢。 - MobileVet

21

通过iOS SDK代码和相关的苹果官方文档。


extension UserDefaults {


    /*!
     NSUserDefaultsSizeLimitExceededNotification is posted on the main queue when more data is stored in user defaults than is allowed. Currently there is no limit for local user defaults except on tvOS, where a warning notification will be posted at 512kB, and the process terminated at 1MB. For ubiquitous defaults, the limit depends on the logged in iCloud user.
     */
    @available(iOS 9.3, *)
    public class let sizeLimitExceededNotification: NSNotification.Name


    // ....
 }   


摘要

  1. 目前本地用户默认值没有限制。
  2. 对于tvOS,当警告通知达到512KB时,进程将被终止;当达到1MB时,进程将被强制退出。
  3. 对于普遍的默认值,限制取决于登录iCloud用户。

18

大家都回答了“有没有限制”的直接问题。然而,我找到这个帖子是为了理解在UserDefaults中存储多少才算过多

如果你正在寻找答案,这里有一个有用的帖子。我发现有用的回答是去项目文件中查看plist文件的大小:

5个对象几乎什么都不是。你会没问题的!


在我的机器上,我有大约28兆字节的数据在我的用户默认中。这根本不会引起任何问题。


从数组的一般编程经验来看,当你进入1000多个元素时,性能开始快速下降,这取决于元素大小。因此,在我的程序中,我不会有问题存储几百个元素。话虽如此,如果我是你,我可能会尽早开始使用sqlite3数据库或coredata。

需要记住的重要事情:

以上内容解除了我对于增长中的默认值(现在大约20-25个)是否会引起问题的担忧。我已经在使用CoreData,所以我正在考虑应该使用哪一个,因为我的允许用户首选项/自定义的数量正在增加。所以,我将继续使用UserDefaults。

然而,正如其他答案指出的那样,该文件将作为一个整体读取和写入。因此,读取20个键/字符串字典和5个键/布尔字典只是为了检索一个字符串...并不完美。尽管如此,如果它不会影响性能并且可以节省大量代码,为什么不呢?


8
正如许多人已经提到的那样:除了物理内存外,我不知道在.plist(例如UserDefaults)中存储数据有任何大小限制。因此,这不是一个关于“多少”的问题。
真正的问题应该是你有多频繁地编写新的/更改的值...这与此类写入会导致的电池耗尽有关。
如果单个值更改,IOS无法避免对“磁盘”的物理写入,以保持数据完整性。关于UserDefaults,这会导致整个文件重新写入磁盘。
这会启动“磁盘”,并使其保持长时间供电,并防止IOS进入低功耗状态。
引用:
《iOS应用程序的能源效率指南》:“最小化数据写入。仅在内容更改时才将其写入文件,并尽可能地聚合更改以进行单个写入。如果只有几个字节发生了变化,请避免写出整个文件。如果您经常更改大文件的小部分,请考虑使用数据库来存储数据。”
读取没有问题,因为所有值都缓存在内存中。
编辑:(2019年7月):我刚刚发现Jeffry Fulton的这篇非常好的博客文章。

https://jeffreyfulton.ca/blog/2018/02/userdefaults-limitations-and-alternatives

他详细描述了用户默认设置的不同方面,并写了一些性能测试。
编码愉快!

7

从 iPadOS 13.1 beta 3 开始,当试图存储一个较大的对象(如图片)时,我现在会看到以下消息:

2019-09-14 11:01:29.634368+0100 MyApp[1686:147223] [用户默认设置] CFPrefsPlistSource<0x283c7d980> (域名: com.example.MyApp, 用户: kCFPreferencesCurrentUser, ByHost: No, 容器: (null), 内容 不需要刷新: No): 在此平台上尝试将 >= 4194304 字节的数据存储在 CFPreferences/NSUserDefaults 中是无效的。 这是 MyApp 或其使用的库中的错误。

但是检索密钥似乎仍然有效。


我也遇到了这个问题!你找到原因/解决方案了吗? - SAHM
@SAHM 不确定原因。我只是将单个图像保存为用户首选项,结果将数据写入了文档文件夹,而不是使用用户默认设置。 - Doug
出于好奇,@Doug,在保存之后你使用了“同步”吗? - SAHM
如果你想聊的话,我对这个有一些想法。 - SAHM
@SAHM 不,我没有调用synchronize。正如文档中所述,“这个方法是不必要的,不应该被使用。” - Doug
我曾经认为在我写这篇文章的时候调用synchronize可能会导致问题。从那时起,我想我在某个地方读到过,尽管可能是旧线程或旧文档,但存储的单个值不应超过4KB。 - SAHM

5
NSUserDefaults中唯一的存储限制是设备存储容量。只要在iOS设备上有可用的存储空间,就可以实际上将数据存储在NSUserDefaults中。键值对存储在一个xml结构文件(.plist)中,并存储在一个应用程序包中。

用户默认系统和键-值存储都是为存储简单数据类型而设计的——如字符串数字日期布尔值、URL数据对象等,这些数据以属性列表的形式存储。使用属性列表还意味着您可以使用数组和字典类型来组织您的首选项数据。也可以通过首先将它们编码成NSData对象来将其他对象存储在属性列表中。


5

NSUserDefaults中存储值没有限制。


我遇到了这个错误:在此平台上尝试存储>= 4194304字节的数据在CFPreferences/NSUserDefaults中是无效的。这是应用程序或其使用的库中的错误。 - Arjun

2
据我所知,NSUserdefaults没有存储限制。

我遇到了这个错误:在此平台上尝试存储 >= 4194304 字节的数据于CFPreferences/NSUserDefaults是无效的。这是MyApp或其使用的库中的错误。 - Arjun

0

无论驱动器上允许的最大文件大小是多少,您都可以使用此代码片段进行检查!

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *myKey = @"key";
int sizeOfFile = 1024; // Fill in proper file size here
NSData myObject;
NSString *someFilePath = @"PathToYourFileHere";

for(int i = 1; i < 9999999999999999; i++)
{
  myObject = [NSData dataWithContentsOfFile:someFilePath];
  [defaults setObject:myObject forKey:[NSString stringWithFormat:@"%@%i", myKey, i]];
  NSLog(@"Iteration: %i, TotalWritten: %i", i, i * sizeOfFile);
}

6
FYI,int 值无法达到那么高的范围,因此在循环中迭代次数超过大约 21.5 亿之后,你将遇到溢出问题。不过,在那之前,你会先遇到 sizeOfFile 的溢出问题。 - Stonz2

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