存在跨进程通信问题的 NSUserDefaults 还能用吗?

在阅读酷酷的哀殿从底层分析一下存在跨进程通信问题的 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:https://developer.apple.com/library/archive/documentation/Miscellaneous/Reference/MobileDeviceManagementProtocolRef/1-Introduction/Introduction.html#//apple_ref/doc/uid/TP40017387
  2. Mobile Device Management Protocol Reference:https://developer.apple.com/library/archive/documentation/Miscellaneous/Reference/MobileDeviceManagementProtocolRef/1-Introduction/Introduction.html#//apple_ref/doc/uid/TP40017387
  3. Preferences and Settings Programming Guide:https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/UserDefaults/Introduction/Introduction.html#//apple_ref/doc/uid/10000059i
发表评论
留言与评论(共有 0 条评论) “”
   
验证码:

相关文章

推荐文章