如何在Swift中使用KVO for UserDefaults?

Sti*_*Sti 16 key-value-observing foundation ios swift

我正在重写应用程序的一部分,并找到了这段代码:

fileprivate let defaults = UserDefaults.standard

func storeValue(_ value: AnyObject, forKey key:String) {
    defaults.set(value, forKey: key)
    defaults.synchronize()

    NotificationCenter.default.post(name: Notification.Name(rawValue: "persistanceServiceValueChangedNotification"), object: key)
}
func getValueForKey(_ key:String, defaultValue:AnyObject? = nil) -> AnyObject? {
    return defaults.object(forKey: key) as AnyObject? ?? defaultValue
}
Run Code Online (Sandbox Code Playgroud)

当CMD点击该行时,defaults.synchronize()我看到synchronize计划已弃用.这是在代码中写的:

/*!
     -synchronize is deprecated and will be marked with the NS_DEPRECATED macro in a future release.

     -synchronize blocks the calling thread until all in-progress set operations have completed. This is no longer necessary. Replacements for previous uses of -synchronize depend on what the intent of calling synchronize was. If you synchronized...
     - ...before reading in order to fetch updated values: remove the synchronize call
     - ...after writing in order to notify another program to read: the other program can use KVO to observe the default without needing to notify
     - ...before exiting in a non-app (command line tool, agent, or daemon) process: call CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication)
     - ...for any other reason: remove the synchronize call
     */
Run Code Online (Sandbox Code Playgroud)

据我所知,我的情况下的用法符合第二种描述:写作后同步,以通知其他人.

它建议使用KVO来养卵,但是如何?当我搜索这个时,我发现了一堆稍微旧的Objective-C-examples.观察UserDefaults的最佳做法是什么?

Mic*_*hal 30

从iOS 11 + Swift 4开始,推荐的方式(根据SwiftLint)使用基于块的KVO API.

例:

假设我的用户默认值中存储了一个整数值,并且它被调用greetingsCount.

首先,我需要扩展UserDefaults:

extension UserDefaults {
    @objc dynamic var greetingsCount: Int {
        return integer(forKey: "greetingsCount")
    }
}
Run Code Online (Sandbox Code Playgroud)

这允许我们稍后定义观察的关键路径,如下所示:

var observer: NSKeyValueObservation?

init() {
    observer = UserDefaults.standard.observe(\.greetingsCount, options: [.initial, .new], changeHandler: { (defaults, change) in
        // your change logic here
    })
}
Run Code Online (Sandbox Code Playgroud)

永远不要忘记清理:

deinit {
    observer?.invalidate()
}
Run Code Online (Sandbox Code Playgroud)

  • 这个答案很好。我想指出,方法名称必须与userdefaults对象名称匹配。如果您改为使用`var myGreetingsCount`,那么您将永远不会得到观察者回调。 (5认同)
  • 谢谢你。你的 `observe` 语句中 `greetingsCount` 之前的 `\.` 是什么意思? (2认同)
  • 这在 macOS 中似乎对我不起作用:/。我使用的是 App Group 用户默认组而不是 StandardUserDefaults。观察者闭包在初始化时被调用一次(给出 nil 值),然后在发生变化时不再调用。此处评论中的类似问题:/sf/ask/3109748001/ (2认同)
  • 您不必清理观察者。参见:/sf/answers/3261434081/ (2认同)

cod*_*ter 8

摘自David Smith的博客 http://dscoder.com/defaults.html https://twitter.com/catfish_man/status/674727133017587712

如果一个进程设置了一个共享的默认值,然后通知另一个进程读取它,那么您可能处于剩下的极少数情况之一中,在以下情况下调用-synchronize方法很有用:-synchronize充当“屏障”,因为它保证返回后,读取该默认值的任何其他进程将看到新值,而不是旧值。

对于在iOS 9.3和更高版本/ macOS Sierra和更高版本上运行的应用程序,即使在这种情况下,也不需要(或建议)-synchronize,因为默认值的键值观察现在在各个进程之间起作用,因此读取过程可以直接监视改变的价值。结果,在那些操作系统上运行的应用程序通常不应调用同步。

因此,在大多数情况下,您无需设置为调用同步。它由KVO自动处理。

为此,您需要在要处理persistanceServiceValueChangedNotification通知的类中添加观察者。假设您要设置名称为“ myKey”的密钥

在班上添加观察者可能是viewDidLoad等

 UserDefaults.standard.addObserver(self, forKeyPath: "myKey", options: NSKeyValueObservingOptions.new, context: nil)
Run Code Online (Sandbox Code Playgroud)

处理观察者

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {

    //do your changes with for key
}
Run Code Online (Sandbox Code Playgroud)

同时删除您的观察者 deinit


Dig*_*ech 5

对于将来寻找答案的任何人,didChangeNotification如果您希望收到所有更新(无论使用 KVO 的流程如何),则仅当对同一流程进行更改时才会发布。

苹果文档

当在当前流程之外进行更改或普遍存在的默认值发生更改时,不会发布此通知。您可以使用键值观察来注册感兴趣的特定键的观察者,以便收到所有更新的通知,无论更改是在当前流程之内还是之外进行。

以下是演示 Xcode 项目的链接,该项目展示了如何在 UserDefaults 上设置基于块的 KVO。