我正在组装一个类,它有一个由枚举定义的状态,以及一个只读属性"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否则会提供的类型安全性吗?
文斯.
也许这仅适用于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 }
别处:
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
输出:
Observed state change to Ready (because .Initial was specified)
Observed state change to Set
Observed state change to Go
我刚才遇到了同样的问题.最后,我使用了枚举状态,并添加了一个额外的'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 } } }
额外:
如果您正在编写正在Swift中进行观察的类,这里有一个方便的实用程序类来消除一些痛苦.好处是:
您的观察者不需要继承NSObject.
观察回调代码作为闭包而不是必须实现observeValueForKeyPath:BlahBlah ...
没有必要确保你删除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)") } } }
请注意[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 } }