swift - 用于观察 Swift 5 变化的通用协议
问题描述
努力用 Swift 5 编写下面的 Java 示例。
一般来说,我想要一个Observable
可以被多个其他协议采用的协议。我需要将这些协议作为函数参数的类型,以便这些函数可以添加额外的观察者。
在 Java 中,这很容易做到。代码打印出来:
Observer 1 changed to 10
Observer 2 changed to 10
,
interface Observable<O> {
void addObserver(O observer);
}
interface Settings extends Observable<SettingsObserver> {
void setInterval(int interval);
}
interface SettingsObserver {
void intervalChanged(int interval);
}
class AppSettings implements Settings {
private List<SettingsObserver> observers = new ArrayList<>();
@Override public void addObserver(SettingsObserver observer) { observers.add(observer); }
@Override public void setInterval(int interval) { observers.forEach(observer -> observer.intervalChanged(interval)); }
}
class Observer1 implements SettingsObserver {
@Override public void intervalChanged(int interval) {
System.out.println("Observer 1 changed to " + interval);
}
}
class Observer2 implements SettingsObserver {
@Override public void intervalChanged(int interval) {
System.out.println("Observer 2 changed to " + interval);
}
}
class Main {
public static void main(String[] args) {
Observer1 observer1 = new Observer1();
Settings settings = new AppSettings();
settings.addObserver(observer1);
Main main = new Main();
main.run(settings);
}
void run(Settings settings) {
Observer2 observer2 = new Observer2();
settings.addObserver(observer2);
settings.setInterval(10);
}
}
解决方案
虽然创建一个可以添加自己的 observables 的通用包装器很简单,但您应该使用两种本机解决方案。
通知。
当值更改时,使用 发送通知
NotificationCenter.default
。观察者应该收听这些通知。通知是生态系统的重要组成部分:class AppSettings { enum Notifications { static let intervalChanged = Notification.Name("AppSettingsIntervalChangedNotification") } var interval: TimeInterval = 0 { didSet { NotificationCenter.default.post(name: Notifications.intervalChanged, object: self) } } } let settings = AppSettings() let observer = NotificationCenter.default.addObserver( forName: AppSettings.Notifications.intervalChanged, object: settings, queue: nil ) { [weak settings] _ in guard let settings = settings else { return } print(settings.interval) } settings.interval = 10
键值观察 (KVO)
如果您从 继承您的对象
NSObject
,您可以简单地将直接观察者添加到任何 Obj-C 兼容值:class AppSettings: NSObject { @objc dynamic var interval: TimeInterval = 0 } let settings = AppSettings() let observer: NSKeyValueObservation = settings.observe(\.interval, options: .new) { _, change in print(change.newValue) } settings.interval = 10
只是为了完整起见,这里有一个简单的通用观察者:
class Observable<ValueType> {
typealias Observer = (ValueType) -> Void
var observers: [Observer] = []
var value: ValueType {
didSet {
for observer in observers {
observer(value)
}
}
}
init(_ defaultValue: ValueType) {
value = defaultValue
}
func addObserver(_ observer: @escaping Observer) {
observers.append(observer)
}
}
class AppSettings {
let interval: Observable<TimeInterval> = Observable(0)
}
let settings = AppSettings()
settings.interval.addObserver { interval in
print(interval)
}
settings.interval.value = 10
请注意,我所有的观察者都是简单的闭包。Java 使用对象作为观察者的原因主要是由于 Java 的限制。Swift 中不需要Observable
orObserver
协议。
推荐阅读
- git - 如何防止来自 github 的 pip install 添加虚假斜线并失败?
- r - 如何将数据框的元素映射到 R 中的另一个数据框?
- node.js - 在 mono-repo 节点 js 项目上应用 semver
- ruby-on-rails - 验证数组 Active Record 中的包含时出错
- r-markdown - 如何为使用 rmarkdown 创建的 PowerPoint 文件中的图像添加替代文本(用于辅助功能)?
- c++ - O(1) 追加和 O(1) 随机访问数据结构
- javascript - 反应:输入不反映变化
- nginx - 如何为标准和 www 设置 SSL。在 nginx 中使用 Let's Encrypt 的域?
- teradata - 如何从一个非常大的表中获取所有数据?
- python - 为什么用 Dask 运行 Sklearn 机器学习不会产生并行性?