通常使用UserDefaults的方法有两种。
func applicationDidFinishLaunching(_ aNotification: Notification) {
let dic = ["test":true]
let id = Bundle.main.bundleIdentifier!
// 1
UserDefaults.standard.setPersistentDomain(dic, forName: id)
// 2
UserDefaults.standard.register(defaults: dic)
}
我们知道,PersistentDomain
是永久的,会写入到磁盘。而register
是临时的,每次程序启动都需要重新加载。因此,上面的代码有一些问题,需要改为
func applicationDidFinishLaunching(_ aNotification: Notification) {
let dic = ["test":true]
let id = Bundle.main.bundleIdentifier!
if let _ = UserDefaults.standard.persistentDomain(forName: id) {
}
else {
UserDefaults.standard.setPersistentDomain(dic, forName: id)
}
UserDefaults.standard.register(defaults: dic)
}
即,必须先确定没有这个PersistentDomain
,然后才能注册。当重置设置为默认时,二者的代码也有一些差异。
let dic = ["test":true]
let id = Bundle.main.bundleIdentifier!
// 1
UserDefaults.standard.removePersistentDomain(forName: id)
UserDefaults.standard.setPersistentDomain(dic, forName: id)
// 2
UserDefaults.standard.resetStandardUserDefaults()
上面代码1是大家经常在网上看到的,但是它实际存在问题,是不正确的。这段代码表面看起来似乎没什么问题,第一步是删除所有设置,第二部是将设置设为默认。但是,如果你考虑到通知的,就会明白了。假设我们有一个注册到UserDefaults.didChangeNotification
的通知。那么第一步就会发出通知,第二步还会再发一次。但是,由于第一步实际上是删除了所有的设置,此时程序有极大的可能会出错。
如果要解决这个问题,上面的代码可能要改为先解除注册的通知,然后删除所有设置,然后再注册通知,再将设置改为默认。但是且慢,我们真的需要这么做吗?为什么一定要清空才能再设置呢?难道不是直接设置就可以了吗?是的,其实直接设置就可以了,完全没必要清空。这样代码也就变成了下面这样:
let dic = ["test":true]
let id = Bundle.main.bundleIdentifier!
// 1
UserDefaults.standard.setPersistentDomain(dic, forName: id)
// 2
UserDefaults.standard.resetStandardUserDefaults()
那么方法2是怎么回事?可不可以也采用直接设置的方式?比如UserDefaults.standard.register(defaults: dic)
。实际上是不可以的。这涉及到UserDefaults
的原理。在实际使用中,系统是将registration domain
和其它domain
联合使用的。苹果的文档这样写到。
The registration domain defines the set of default values to use if a given preference is not set explicitly in one of the other domains.
如果其它domain没有设置某个设置,就使用registration domain
定义的设置,即它实际是fail safe
的默认值。
实际上registration domain
,是临时的设置,你在每次启动程序时,都需要设置它。但是,如果用户将registration domain
中含有的项的值改变了,系统就会自动将改变的内容写入到一个以你的程序命名的plist
中。这里的值的优先级别,要比registration domain
里的值的级别高,程序会以这里的为准。当然,你也可以通过直接写入PersistentDomain
方法来修改这里的值。但是我们一般不会这么做。而是使用UserDefaults
的一系列set
方法,当使用set
方法时,系统会自动将设置写入到plist
中。
registration domain
的好处
使用- 用户仅能见到非默认的设置,如果有些设置你不打算让用户知道或修改,这样更安全。
- 注册和删除的方式更加优雅。
- 添加新设置时更为方便,无需考虑程序版本。
结论
正确的使用UserDefaults
的方法是:
- 在程序每次启动时调用
registration domain
来写如程序的默认值 - 当默认值被被用户修改时,调用
UserDefaults
的系列set
方法来调用 - 除非要写入到其它的
plist
,我们一般不必使用PersistentDomain
。