用于观察 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);
    }
}

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

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

根据您的需求,您可以使用 Swift 中的属性观察器。 它允许您在属性要更改或已更改时采取措施。 它也比完全可观察的类型复杂。

以下是 Swift 手册中 Apple 的示例:

class StepCounter {
    var totalSteps: Int = 0 {
        willSet(newTotalSteps) {
            print("About to set totalSteps to (newTotalSteps)")
        }
        didSet {
            if totalSteps > oldValue  {
                print("Added (totalSteps - oldValue) steps")
            }
        }
    }
}
let stepCounter = StepCounter()
stepCounter.totalSteps = 200
// About to set totalSteps to 200
// Added 200 steps
stepCounter.totalSteps = 360
// About to set totalSteps to 360
// Added 160 steps
stepCounter.totalSteps = 896
// About to set totalSteps to 896
// Added 536 steps

您可能希望使用 didSet(( 函数。 您还可以在观察器中调用另一个函数。

如果您不想使用 RxSwift 或 Apple 的新 Combine 等框架,您还可以使用属性观察器编写一个简单的类似可观察的类。

下面是一个仅使用闭包而不是类的简单示例:

class ClassToWatch {
    typealias ObservingFunc = (ClassToWatch) -> Void
    private var observers: [ObservingFunc] = []
    func addObserver(_ closure: @escaping ObservingFunc) {
        observers.append(closure)
    }
    private func valueChanged() {
        observers.forEach { observer in
            observer(self)
        }
    }
    var value1: Int = 0 {
        didSet {
            valueChanged()
        }
    }
    var value2: String = "" {
        didSet {
            valueChanged()
        }
    }
}
var myclass = ClassToWatch()
myclass.addObserver { object in
    print("Observer 1: (object.value1) (object.value2)")
}
myclass.addObserver { object in
    print("Observer 2: (object.value1) (object.value2)")
}
myclass.value1 = 3
myclass.value2 = "Test"

你的 Java 代码可以直接翻译成 Swift 代码。这是我的翻译,带有一定程度的"快速化":

protocol Observable {
    associatedtype ObserverType
    func addObserver(_ observer: ObserverType)
}
protocol Settings : Observable where ObserverType == SettingsObserver {
    var interval: Int { get set }
}
protocol SettingsObserver {
    func intervalDidChange(newValue: Int)
}
class Observer1 : SettingsObserver {
    func intervalDidChange(newValue: Int) {
        print("Observer 1 changed to (newValue)")
    }
}
class Observer2 : SettingsObserver {
    func intervalDidChange(newValue: Int) {
        print("Observer 2 changed to (newValue)")
    }
}
class AppSettings: Settings {
    var interval: Int = 0 {
        didSet {
            observers.forEach { $0.intervalDidChange(newValue: interval) }
        }
    }
    private var observers: [SettingsObserver] = []
    func addObserver(_ observer: SettingsObserver) {
        observers.append(observer)
    }
}
let settings = AppSettings()
settings.addObserver(Observer1())
settings.addObserver(Observer2())
settings.interval = 10

尽管Observable不能用作参数类型,但从中派生的协议(也指定了关联的类型(可以。

您可以更进一步,使SettingsObserver的类型别名为 (Int) -> Void 。这样你就不需要所有这些不同的ObserverX类。

typelias SettingsObserver = (Int) -> Void

然后,addObserver调用将变为:

settings.addObserver { print("Observer 1 changed to ($0)") }
settings.addObserver { print("Observer 2 changed to ($0)") }

didSet中的呼叫将更改为:

observers.forEach { $0(interval) }

另外,我不明白为什么Settings存在。你不能直接AppSettings符合Observable吗?我的意思是,我知道程序接口的想法等等,但 IMO 这有点太多了......

最新更新