Swift 在observeValueForKeyPath中使用开关
我试图通过将嵌套的if/else语句的典型长字符串替换为单个switch语句来提高KVO observeValueForKeyPath实现的易读性 到目前为止,唯一有效的方法是:Swift 在observeValueForKeyPath中使用开关,swift,switch-statement,key-value-observing,Swift,Switch Statement,Key Value Observing,我试图通过将嵌套的if/else语句的典型长字符串替换为单个switch语句来提高KVO observeValueForKeyPath实现的易读性 到目前为止,唯一有效的方法是: private let application = UIApplication.sharedApplication() switch (object!, keyPath!) { case let (object, "delegate") where object as? UIApplication
private let application = UIApplication.sharedApplication()
switch (object!, keyPath!) {
case let (object, "delegate") where object as? UIApplication === application:
appDelegate = application.delegate
break
...
default:
super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)
}
如果说有什么区别的话,这比:
if object as? UIApplication === application && keyPath! == "delegate" {
}
else {
}
有没有人有一个好的模式来使用switch-inobserveValueForKeyPath
(和类似的方法)
编辑:与下面@critik的问题相关,这里有更多的代码来演示仅使用开关(对象为!NSObject,keyPath!){
:
private let application = UIApplication.sharedApplication()
private var appDelegate : UIApplicationDelegate?
private var rootWindow : UIWindow?
public override func observeValueForKeyPath(
keyPath: String?,
ofObject object: AnyObject?,
change: [String : AnyObject]?,
context: UnsafeMutablePointer<Void>) {
switch (object as! NSObject, keyPath!) {
case (application, "delegate"):
appDelegate = application.delegate
(appDelegate as? NSObject)?.addObserver(self, forKeyPath: "window", options: [.Initial], context: nil)
break
case (appDelegate, "window"):
rootWindow = appDelegate?.window?.flatMap { $0 }
break
case (rootWindow, "rootViewController"):
rebuildViewControllerList(rootWindow?.rootViewController)
break
default:
super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)
}
}
private let application=UIApplication.sharedApplication()
私有var appDelegate:UIApplicationDelegate?
私有var根窗口:UIWindow?
公共覆盖函数observeValueForKeyPath(
键路径:字符串?,
ofObject对象:任何对象?,
更改:[字符串:AnyObject]?,
上下文:unsafemeutablepointer){
开关(对象为!NSObject,keyPath!){
案例(申请,“代表”):
appDelegate=application.delegate
(appDelegate作为?NSObject)?.addObserver(self,forKeyPath:“窗口”,选项:[.Initial],上下文:nil)
打破
案例(appDelegate,“窗口”):
rootWindow=appDelegate?.window?.flatMap{$0}
打破
案例(rootWindow,“rootViewController”):
重建IEWControllerList(rootWindow?.rootViewController)
打破
违约:
super.observeValueForKeyPath(键路径,of对象:object,change:change,context:context)
}
}
元组上的开关如何:
switch (object as! NSObject, keyPath!) {
case (application, "delegate"):
appDelegate = application.delegate
...
default:
super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)
}
注1。尽管我在Swift中再次使用强制内容(downcast、unwrapps等),但您可以安全地强制downcast到NSObject
,而不会发生崩溃,根据,KVO仅适用于NSObject
子类
注2。在Swift中,您也不需要使用中断
,这也会将代码至少缩短一行:)这并不能真正解决在observeValueForKey
中使用开关
的问题,但它确实演示了我如何简化问题空间以消除冗长的代码
我创建了一个实用程序类,KVOValueWatcher
,它允许我在对象的单个属性上添加(和删除)KVO观察:
public class KVOValueWatcher<ObjectType:NSObject, ValueType:NSObject> : NSObject {
public typealias OnValueChanged = (ValueType?, [String:AnyObject]?) -> ()
let object : ObjectType
let keyPath : String
let options : NSKeyValueObservingOptions
let onValueChanged : OnValueChanged
var engaged = false
public init(object:ObjectType, keyPath:String, options : NSKeyValueObservingOptions = [], onValueChanged: OnValueChanged) {
self.object = object
self.keyPath = keyPath
self.onValueChanged = onValueChanged
self.options = options
super.init()
engage()
}
deinit {
if(engaged) {
print("KVOValueWatcher deleted without being disengaged")
print(" object: \(object)")
print(" keyPath: \(keyPath)")
}
disengage()
}
public func engage() {
if !engaged {
self.object.addObserver(self, forKeyPath: keyPath, options: options, context: nil)
engaged = true
}
}
public func disengage() {
if engaged {
self.object.removeObserver(self, forKeyPath: keyPath)
engaged = false
}
}
override public func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
self.onValueChanged(((object as? NSObject)?.valueForKeyPath(keyPath!) as? ValueType), change)
}
}
更改的主要原因是,维护所有不同的观察结果并正确添加和删除它们,这将成为一场噩梦。添加包装类可以消除问题,组可以监视属性以及属性在同一位置更改时要采取的操作。我尝试了一系列强制转换变量iants,但没有尝试NSObject。不幸的是,如果某些目标是可选的,则这似乎不起作用。“UIApplicationLegate类型的表达式模式与NSObject类型的值不匹配”@DavidBerry你能发布失败的代码吗?也许我能找到解决方案:)我想我用足够多的代码更新了它以查看问题。@DavidBerry我用第二个开关的解决方案更新了我的答案,但这段代码编译了,但它没有做同样的事情。而不是检查对象==application
,您的代码检查对象是否为UIApplication
,然后将其分配给名为application的新变量,即,它只执行类型检查。要了解我的意思,请将其更改为case let(应用程序为UIApplication,“委托”):
看看它是否还能编译。你真正想说的是整个KVO体系结构都很糟糕。这是真的,但这不是什么新鲜事。我们在这条路上走来走去了很多次。有很多明显的解决办法,但最终很难避免一个巨大的瓶颈,所有的东西都通过这一个方法,还有一个巨大的切换“这是处理它的标准方法。@matt现在更让我恼火的是它在iOS上的实现不完全,或者可能使用不完全)。系统对象上有许多属性无法有效地观察,UIViewController。例如presentedViewController
。谢天谢地。你知道KVO是如何工作的吗?它可以到达r瞄准你的对象并旋转它,用另一个对象替换你的对象!这是魔鬼的工作。事实上,我们有时不得不使用它,这实际上是个问题。是的,我从很早的时候就开始使用KVO。从概念上讲,它是一个非常强大的机制,尤其是在MacOS上,你可以将视图直接绑定到roperty。我经常为在iOS上实现这一点而感到惋惜,因为我已经将解决方案拼凑在一起,至少有几次可以做到这一点:)“你可以将视图直接绑定到属性”当然,我知道。我的应用程序严重依赖于此。但我当然不知道它们是如何工作的
rootWindowWatcher = KVOValueWatcher(object: applicationDelegate as! NSObject, keyPath: "window", options: [.Initial]) { window, changes in
self.rootViewWatcher?.disengage()
self.rootViewWatcher = nil
if let window = window {
self.rootViewWatcher = KVOValueWatcher(object: window, keyPath: "rootViewController", options: [.Initial]) {
[unowned self] rootViewController, changes in
self.rootViewController = rootViewController
self.rebuildActiveChildWatchers()
}
}
}