首页 > 解决方案 > 用于观察 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);
    }
}

标签: swiftgenericsprotocolsswift5

解决方案


虽然创建一个可以添加自己的 observables 的通用包装器很简单,但您应该使用两种本机解决方案。

  1. 通知。

    当值更改时,使用 发送通知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
    
  2. 键值观察 (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
    

    https://developer.apple.com/documentation/swift/cocoa_design_patterns/using_key-value_observing_in_swift

只是为了完整起见,这里有一个简单的通用观察者:

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 中不需要ObservableorObserver协议。


推荐阅读