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.但它确实有一些限制.
NSObject
(直接或间接)扩展包含类@objc
Int
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中进行观察的类,这里有一个方便的实用程序类来消除一些痛苦.好处是:
调用实用程序类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)