是否可以在Swift中的struct变量上添加观察者?

Ana*_*ore 1 key-value-observing swift

我需要在结构类型的变量中跟踪更新。是否可以在Swift中的struct变量上添加观察者?

例:

struct MyCustomStruct {
    var error:Error?
    var someVar:String?
}

class MyClass{
  var myCustomStruct:MyCustomStruct?
}
Run Code Online (Sandbox Code Playgroud)

我想在myCustomStruct变量上添加一个观察者。

Rob*_*Rob 6

标准的Swift“ 属性观察者 ”(didSetwillSet)旨在让类型观察其自身属性的变化,而不是让外部对象添加自己的观察者。而且,KVO仅支持dynamic@objc属性NSObject子类(如在Swift使用键值观察中概述的那样),它确实支持外部观察者。

因此,struct如其他人所指出的那样,如果要让外部对象观察内的变化,则必须使用Swift didSet等创建自己的观察器机制。但是,您可以编写一个泛型类型来代替您自己逐个属性地实现该目的。例如,

struct Observable<T> {
    typealias Observer = String

    private var handlers: [Observer: (T) -> Void] = [:]

    var value: T {
        didSet {
            handlers.forEach { $0.value(value) }
        }
    }

    init(_ value: T) {
        self.value = value
    }

    @discardableResult
    mutating func observeNext(_ handler: @escaping (T) -> Void) -> Observer {
        let key = UUID().uuidString as Observer
        handlers[key] = handler
        return key
    }

    mutating func remove(_ key: Observer) {
        handlers.removeValue(forKey: key)
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以执行以下操作:

struct Foo {
    var i: Observable<Int>
    var text: Observable<String>

    init(i: Int, text: String) {
        self.i = Observable(i)
        self.text = Observable(text)
    }
}

class MyClass {
    var foo: Foo

    init() {
        foo = Foo(i: 0, text: "foo")
    }
}

let object = MyClass()
object.foo.i.observeNext { [weak self] value in   // the weak reference is really only needed if you reference self, but if you do, make sure to make it weak to avoid strong reference cycle
    print("new value", value)
}
Run Code Online (Sandbox Code Playgroud)

然后,当您更新属性时(例如,如下所示),将调用观察者处理程序闭包:

object.foo.i.value = 42
Run Code Online (Sandbox Code Playgroud)

值得注意的是,诸如BondRxSwift之类的框架提供了这种功能,还有更多功能。


Rob*_*ler 5

对于变量,您可以使用两个默认观察者

  • willSet - 表示变量将被设置为新值之前的时刻

  • didSet - 表示设置变量的时刻


同样在观察者中,您可以使用两个值。当前状态下的当前变量,以及取决于观察者的常数

struct Struct {
    var variable: String {
        willSet {
            variable  // before set 
            newValue  // after set,  immutable
        }
        didSet {
            oldValue  // before set, immutable
            variable  // after set
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以对任何其他存储属性执行相同的操作,因此您也可以将其用于类中的 struct 变量

class Class {
    var myStruct: Struct? {
        didSet {
            ...
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

例如,您还可以在 do set Observer of variable post notice 中使用特定名称

didSet {
    NotificationCenter.default.post(name: Notification.Name("VariableSet"), object: nil)
}
Run Code Online (Sandbox Code Playgroud)

然后您可以添加某个类作为观察者以使用此名称进行通知

class Class {
    init() {
        NotificationCenter.default.addObserver(self, selector: #selector(variableSet), name: Notification.Name("VariableSet"), object: nil)
    }

    deinit {
        NotificationCenter.default.removeObserver(self, name: Notification.Name("VariableSet"), object: nil)
    }

    @objc func variableSet() {
        ...
    }
}
Run Code Online (Sandbox Code Playgroud)