Стандартные Swift « наблюдатели свойств » (didSet
и willSet
) предназначены для того, чтобы тип мог наблюдать изменения своих собственных свойств, но не для того, чтобы внешние объекты могли добавлять своих собственных наблюдателей. А 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)
}
}
Тогда вы можете делать такие вещи, как:
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)
}
И тогда, когда вы обновите свойство, например, как показано ниже, будет закрыто ваше обработчик наблюдателя:
object.foo.i.value = 42
Стоит отметить, что фреймворки, такие как Bond или RxSwift , предлагают такую функциональность, а также многое другое.