二、NSUserDefaults
NSUserDefaults官方
它底层是一个xml格式的plist文件保存在应用程序的沙箱中。
文档中给了开发者提供了两个例子:
- 可以存储一些应用中常用的测量单位
- 存储用户喜欢的多媒体播放速度
其实很好理解,如果应用中的多媒体被作为单独模块拆分,我们很容易将用户偏好这部分逻辑方便的放置在模块内部,而实际上,它也确实应该被放在那里。一般来说,小规模数据,弱业务相关数据,都可以放到NSUserDefault里面,内容比较多的数据,强业务相关的数据就不太适合NSUserDefault了。
在应用程序运行期间数据库中的数据可以多线程读取这个没有问题,但是设置值的时候,NSUserDefaults会在使用线程中同步的修改这个值,但是其实真正持久化到数据库的时候是异步的。同时NSUserDefaults也提供了方法用于强制将当前值写入持久存储。
同时需要注意的是不要在应用程序中自己打开那些存储文件。
Don’t try to access the preferences subsystem directly. Modifying preference property list files may result in loss of changes, delay of reflecting changes, and app crashes. To configure preferences, use the defaults command-line utility in macOS instead.
存储默认对象
对于一些基础的默认数据类型,NSUserDefaults提供了一些便捷方法来设置他们。包括:
- float
- double
- integer
- boolean
- url
NSUserDefaults支持的可存储的对象类型有:
- NSData
- NSString
- NSNumber
- NSDate
- NSArray
- NSDictionary
其中集合中包含的对象也必须是他们中的一员,不行的话就只能归档了。
默认值和域
NSUserDefaults 还有一个 Domain 的概念,当我们调用 NSUserDefaults.standardUserDefaults() 方法时,就会初始化 NSUserDefaults, 并且它默认会包含 5 个 Domain,分别是:
- NSArgumentDomain
- Application
- NSGlobalDomain
- Languages
- NSRegistrationDomain
搜索顺序如下:
NSArgumentDomain -> Application -> NSGlobalDomain -> Languages -> NSRegistrationDomain
关于这部分内容可以参考该文:
NSUserDefaults - 熟悉与陌生
持久化文件引用
其实主要是通过在文件系统生成文件bookmark的做法来防止存放文件URL之后,文件路径被修改。
关于iOS中的URL可以参考这篇文章:
iOS-Foundation NSURL
响应偏好改变
我们可以使用kvo来监听某个默认值的变化。其中key为你存储在用户偏好中的值。
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self
forKeyPath:@"values.key"
options:NSKeyValueObservingOptionNew
context:NULL];
简单点可以写为
[[NSUserDefaults standardUserDefaults] addObserver:self
forKeyPath:@"key"
options:NSKeyValueObservingOptionNew
context:NULL];
关于沙盒的考虑
一个基于沙盒管理的App一般情况下是不能修改其他沙盒下的App的,有两种情况例外:
- macOS 或 iOS App的extensions
- macOS中App包含在一个group中
关于这个部分有一个实际应用。之前开发的应用中有一个功能是收款语音播报。我采用的方案是使用app extension功能进行开发。其中涉及到一个需求,在主应用中有一个控制是否播放语音的开关,在extension中实现了合成语音并播放的功能。
-
开启AppGroup
image.png - 初始化针对该group的用户偏好文件
NSUserDefaults *shareUserDefault = [[NSUserDefaults alloc] initWithSuiteName:@"group.xxxx"];
if (![shareUserDefault boolForKey:@"FirstVoiceManagerProfile"]) {
[shareUserDefault setBool:YES forKey:OPEN_KEY];
[shareUserDefault setBool:YES forKey:@"FirstVoiceManagerProfile"];
}
- 读取共享用户偏好
// 获取共享域的偏好设置
NSUserDefaults *shareUserDefault = [[NSUserDefaults alloc] initWithSuiteName:@"group.xxxx"];
// 解析推送自定义参数userInfo
NSDictionary *userInfo = [self dictionaryWithUserInfo:self.bestAttemptContent.userInfo];
BOOL canSound = [shareUserDefault boolForKey:OPEN_KEY];
NSString *voiceString = nil;
//
if (canSound && userInfo) {
voiceString = [NSString stringWithFormat:@"收款%@元!", userInfo[@"money"]];
}
线程安全
NSUserDefaults是线程安全的。