NSUserDefaults 还能用吗?

2,956 阅读5分钟

欢迎各位 点赞评论,感觉有用的朋友可以关注笔者公众号 iOS 成长指北,持续更新

在阅读哀殿 从底层分析一下存在跨进程通信问题的 NSUserDefaults 还能用吗? 一文中,了解到由于 NSUserDefaults 底层实现中存在直接或者间接的跨进程通信,在主线程同步调用容易发生卡死。

发现已经使用过很久的 NSUserDefaults 了,但是似乎从来没有好好了解过 NSUserDefaults

什么是 NSUserDefaults

NSUserDefaults 是 iOS 开发者常用的持久化工具,通常用于在应用程序的启动过程中持久化存储键值对,通常建议只存储少量的数据。

在运行时,你使用 NSUserDefaults 对象获取应用程序使用的默认值。NSUserDefaults缓存信息,来避免每次获取都需要打开默认的底层数据库。当设置默认值时,它将在进程中同步更改,并异步更改到持久存储和其他进程

一个很多人不知道的点,NSUserDefaults 支持 KVO 获取并且可以选择在通知中心注册 NSUserDefaultsDidChangeNotification 的观察者,以便获得对本地默认数据库的所有更新的通知。

当我们使用 NSUserDefaults 是会存在两个关于设备管理或设备用户管理的特殊处理。如果你的应用程序支持托管环境,那么可以通过 NSUserDefaults 设置用户的身份或者是在教育机构管理的设备上运行的应用程序可以使用 iCloud 密钥值存储区与用户其他设备上的其他实例共享少量数据。

你可以通过查看 《Mobile Device Management Protocol Reference》[1]、《Storing Preferences in iCloud》[2]和《Preferences and Settings Programming Guide》[3] 来获取更多信息。

这为青少年模式的实现提供了一种思路。

支持跨域,或者说支持沙盒跨域。当我们使用 NSUserDefaults 时,我们可以通过使用 suiteName 用于保存在多个应用程序之间共享的值。

NSUserDefaults 是线程安全的,其底层是使用 os_unfair_lock 来保障线程安全, synchronize 函数在项目开发时变得不再那么必要。

NSUserDefaults 存储在哪里

当我们使用 **NSUserDefaults**存储数据时

NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];
[userDefault setObject:@"iOS成长指北" forKey:@"Name"];
[userDefault setObject:@"15" forKey:@"Age"];
[userDefault setObject:@"123456" forKey:@"Password"];

其本质是在沙盒的 Library/Preferences 下生成一个以 Bundle identifier 为名的 .plist 文件并存储数据。文件中的字符和信息是明文的,如果你存储的数据是机密的且重要的,不建议你使用 NSUserDefaults,其安全并不能得到保障。

由于缓存的存在,我们在使用 NSUserDefaults 一般不用过分担忧其读数据的速度,但是我们应该注意 避免数据过多导致内存压力过大

NSUserDefaults 中的域

当我们使用 NSUserDefaults 存储数据时,我们一般使用 standardUserDefaults 来获取当前的单例对象。如果单例对象尚不存在,则会使用包含以下域名称的搜索列表的顺序创建该对象:

  • 仅对于被管理的设备,包含由管理员设置的默认值的域

  • NSArgumentDomain:代表的是命令行参数,可以在 Edit Scheme->Arguments->Arguments Passed On Launch 中添加,格式为-key plistvalue

  • 对于仅由教育机构管理的设备,一个域包含在 iCloud 键值存储中设置的默认值

  • Application:应用程序域,默认设置的值在 Application 域中

  • NSGlobalDomain:全局域,给定用户在所有应用程序之间共享的域。其方法的实现是根据 suiteName 实现的。

    //添加适用域
    - (void)addSuiteNamed:(NSString *)suiteName;
    //添加适用域
    - (void)removeSuiteNamed:(NSString *)suiteName;
    
  • NSRegistrationDomain:一组临时默认值,其值可以由应用程序设置,以确保搜索将始终成功。

我们可以通过使用NSRegistrationDomain 创建默认值来让减少获取值的判断。

使用方法

关于 NSUserDefaults 的使用,我们需要注意什么吗?

  • NSUserDefaults 还能用吗?

    答案是还能用的,只要不要频繁的去修改值。

  • 我们应该用 NSUserDefaults 去存储什么?

    应该去存储一些少量的且并不是十分敏感的数据,例如用户的一些设置,一些标记。

  • 还需要加 synchronize 吗?

    只有第一次初始化或者调用 set... forKey: 相关的方法时,才会触发多进程通信,一旦写入的话由于其多线程安全,对于是否需要加 synchronize 就看个人习惯了,习惯了就加,不习惯就不加。

保存文件的引用

在官方文档上提及的一个关于 setURL:forKey: 的使用,当我们存储文件数据时,可能会发生用户移动该文件,下次打开应用程序时无法定位改文件。你可以使用bookmarkDataWithOptions: inclingresourcevaluesforkeys:relativeToURL:error: 方法来创建 NSURL 书签数据,并使用 setObject:forKey: 方法来持久化它。然后,你可以使用 URLByResolvingBookmarkData:options:relativeToURL:bookmarkDataIsStale:error: 方法来将存储在用户默认值中的书签数据解析为文件 URL 并存储使用

参考资料

  1. Mobile Device Management Protocol Reference
  2. Mobile Device Management Protocol Reference
  3. Preferences and Settings Programming Guide

如果你有任何问题、评论或反馈,请随时联系。如果你愿意,可以通过分享这篇文章来让更多的人发现它。

感谢你阅读本文! 🚀