Swift KVO - 观察枚举属性

Vin*_*van 6 enums key-value-observing swift

我正在组装一个类,它有一个由枚举定义的状态,以及一个只读属性"state",它返回实例的当前状态.我希望使用KVO技术来观察状态的变化,但这似乎不可能:

dynamic var state:ItemState // Generates compile-time error: Property cannot be marked dynamic because its type cannot be represented in Objective-C

我想我可以将每个状态表示为Int或String等,但是有一个简单的替代解决方法可以保留enum否则会提供的类型安全性吗?

文斯.

ɲeu*_*urɳ 16

也许这仅适用于swift 2+,但您可以直接观察枚举属性,而无需引用其rawValue.但它确实有一些限制.

  1. NSObject(直接或间接)扩展包含类
  2. 用枚举标记枚举 @objc
  3. 从中扩展枚举 Int
  4. 将该属性声明为dynamic.
class SomeModel : NSObject {                          // (1) extend from NSObject
    @objc                                             // (2) mark enum with @objc
    enum ItemState : Int, CustomStringConvertible {   // (3) extend enum from Int
        case Ready, Set, Go

        // implementing CustomStringConvertible for example output
        var description : String {
            switch self {
            case .Ready: return "Ready"
            case .Set: return "Set"
            case .Go: return "Go"
            }
        }
    }

    dynamic var state = ItemState.Ready               // (4) declare property as dynamic
}
Run Code Online (Sandbox Code Playgroud)

别处:

class EnumObserverExample : NSObject {
    private let _model : SomeModel

    init(model:SomeModel) {
        _model = model
        super.init()
        _model.addObserver(self, forKeyPath:"state", options: NSKeyValueObservingOptions.Initial, context: nil)
    }
    deinit {
        _model.removeObserver(self, forKeyPath:"state", context: nil)
    }

    override func observeValueForKeyPath(keyPath: String!, ofObject object: AnyObject!, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
        if "state" == keyPath {
            print("Observed state change to \(_model.state)")
        }
    }
}

let model = SomeModel()
let observer = EnumObserverExample(model:model)
model.state = .Set
model.state = .Go
Run Code Online (Sandbox Code Playgroud)

输出:

Observed state change to Ready    (because .Initial was specified)
Observed state change to Set
Observed state change to Go
Run Code Online (Sandbox Code Playgroud)


Mik*_*ard 14

我刚才遇到了同样的问题.最后,我使用了枚举状态,并添加了一个额外的'raw'属性,该属性由主状态属性上的属性观察者设置.

您可以KVO'原始'属性,但在更改时引用真实的枚举属性.

这显然有点像黑客,但对我来说,这比完全放弃枚举并失去所有好处要好.

例如.

class Model : NSObject {

    enum AnEnumType : String {
        case STATE_A = "A"
        case STATE_B = "B"
    }

    dynamic private(set) var enumTypeStateRaw : String?

    var enumTypeState : AnEnumType? {
        didSet {
            enumTypeStateRaw = enumTypeState?.rawValue
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

额外:

如果您正在编写正在Swift中进行观察的类,这里有一个方便的实用程序类来消除一些痛苦.好处是:

  1. 您的观察者不需要继承NSObject.
  2. 观察回调代码作为闭包而不是必须实现observeValueForKeyPath:BlahBlah ...
  3. 没有必要确保你删除Observer,它会为你照顾.

调用实用程序类KVOObserver,示例用法是:

class ExampleObserver {

    let model : Model
    private var modelStateKvoObserver : KVOObserver?

    init(model : Model) {

        self.model = model

        modelStateKvoObserver = KVOObserver.observe(model, keyPath: "enumTypeStateRaw") { [unowned self] in
            println("new state = \(self.model.enumTypeState)")
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意[unowned self]捕获列表中的参考周期.

这是KVOObserver......

class KVOObserver: NSObject {

    private let callback: ()->Void
    private let observee: NSObject
    private let keyPath: String

    private init(observee: NSObject, keyPath : String, callback: ()->Void) {
        self.callback = callback
        self.observee = observee
        self.keyPath = keyPath;
    }

    deinit {
        println("KVOObserver deinit")
        observee.removeObserver(self, forKeyPath: keyPath)
    }

    override func observeValueForKeyPath(keyPath: String,
        ofObject object: AnyObject,
        change: [NSObject : AnyObject],
        context: UnsafeMutablePointer<()>) {
            println("KVOObserver: observeValueForKey: \(keyPath), \(object)")
            self.callback()
    }

    class func observe(object: NSObject, keyPath : String, callback: ()->Void) -> KVOObserver {
        let kvoObserver = KVOObserver(observee: object, keyPath: keyPath, callback: callback)
        object.addObserver(kvoObserver, forKeyPath: keyPath, options: NSKeyValueObservingOptions.New | NSKeyValueObservingOptions.Initial, context: nil)
        return kvoObserver
    }
}
Run Code Online (Sandbox Code Playgroud)