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

时间:2019-01-17 08:29:01

标签: swift key-value-observing

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

示例:

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

class MyClass{
  var myCustomStruct:MyCustomStruct?
}

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

5 个答案:

答案 0 :(得分:4)

标准的Swift“ property observers”(didSetwillSet)旨在让类型观察其自身属性的变化,而不是让外部对象添加自己的观察者。而且确实支持外部观察者的KVO仅适用于dynamic@objc属性NSObject子类(如Using Key-Value Observing in 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

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

答案 1 :(得分:3)

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

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

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


还可以在观察者中使用两个值。当前状态下的电流变量为常数,视观察者而定

struct Struct {
    var variable: String {
        willSet {
            variable  // before set 
            newValue  // after set,  immutable
        }
        didSet {
            oldValue  // before set, immutable
            variable  // after set
        }
    }
}

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

class Class {
    var myStruct: Struct? {
        didSet {
            ...
        }
    }
}

例如,您还可以在具有特定名称的变量后通知的set set观察器中

didSet {
    NotificationCenter.default.post(name: Notification.Name("VariableSet"), object: nil)
}

然后您可以使用该名称将某些类添加为通知的观察者

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() {
        ...
    }
}

答案 2 :(得分:0)

尝试此操作,首先创建带有操作变量的结构,然后在创建该结构的对象时,在所需操作上设置操作参数。例如。

struct testStruct {
var action: (()->())?
var variable: String? {
    didSet {
        self.action?()
    }
}

}

在您的主代码内-主类

var testS = testStruct()
    testS.action = {
        print("Hello")
    }
    testS.variable = "Hi"

设置testS.variabe =“ Hi”时,它将调用print(“ Hello”)

答案 3 :(得分:-1)

当然,您可以使用didSet,willSet或set。这是一个示例:

class MyClass {
    var someStruct: SomeStruct {
        didSet {
            print("structure variable was changed")
        }
    }
}

如果要通知另一个对象有关更改的信息,则可以添加回调。

答案 4 :(得分:-2)

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

class MyClass{
    var myCustomStruct:MyCustomStruct? {
        didSet{
            print("my coustomeSruct changed")
        }
    }
}

let aClass = MyClass()
aClass.myCustomStruct?.someVar = " test"
 //prints:my coustomeSruct changed