【问题标题】:Using switch in observeValueForKeyPath在 observeValueForKeyPath 中使用 switch
【发布时间】:2016-01-19 17:33:36
【问题描述】:

我试图通过用单个 switch 语句替换典型的长字符串嵌套 if/else 语句来提高我的 KVO observeValueForKeyPath 实现的易读性。

到目前为止,唯一真正起作用的是:

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 {

    }

有没有人有一个在observeValueForKeyPath(和类似方法)中使用switch的好模型

编辑:与下面@critik 的问题相关,这里有更多代码来演示仅使用switch (object as! 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)
    }
}            

【问题讨论】:

  • 你真正想说的是整个 KVO 架构很糟糕。这是真的,但这不是新消息。我们在这条路上走来走去很多次。有许多明显的变通方法,但最终很难避免一个巨大的瓶颈,即一切都通过这种方法,而大规模切换是处理它的标准方法。
  • @matt 现在对我来说更烦人的是它在 iOS 上的实现不完全,或者可能不完全使用)。系统对象上有许多属性无法有效观察,例如UIViewController.presentedViewController
  • 心存感激。你知道 KVO 是如何工作的吗?它直接进入您的对象并混合它,用另一个对象代替您的对象!这是魔鬼的工作。事实上,我们有时不得不使用它是问题所在。
  • 是的,我从一开始就一直在使用 KVO。从概念上讲,它是一种非常强大的机制,尤其是在 MacOS 上,您可以将视图直接绑定到属性。我经常感叹在 iOS 上能够做到这一点,以至于我已经组合了一些解决方案,让我至少可以在几个场合做到这一点:)
  • “您可以将视图直接绑定到属性” 当然,我知道。我有严重依赖于此的应用程序。但我当然无法弄清楚它们是如何工作的。 :)

标签: swift switch-statement key-value-observing


【解决方案1】:

在元组上使用switch 怎么样:

switch (object as! NSObject, keyPath!) {

case (application, "delegate"):
    appDelegate = application.delegate

...

default:
    super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)
}

注意 1. 虽然我再次在 Swift 中强制执行某些操作(向下转换、展开等),但您可以安全地强制向下转换到 NSObject 而不会崩溃,根据 this SO question, KVO 仅适用于 NSObject 子类。

注意 2. 在 Swift 中您也不需要 break,这也会将您的代码缩短至少一行 :)

【讨论】:

  • 我尝试了一堆转换变体,但没有尝试 NSObject。不幸的是,如果某些目标是可选的,这似乎不起作用。 “'UIApplicationDelegate 类型的表达式模式?'无法匹配“NSObject”类型的值”
  • @DavidBerry 你能发布失败的代码吗?也许我会找到解决方案:)
  • 我认为我更新了足够多的代码来发现问题。
  • @DavidBerry 我用第二个switch的解决方案更新了我的答案@
  • 这段代码可以编译,但它不会做同样的事情。您的代码不是检查是否为object == application,而是检查是否为object is UIApplication,然后将其分配给一个名为application 的新变量,即,它只进行类型检查。要了解我的意思,请将其更改为 case let (app as UIApplication, "delegate"): 并查看它仍然可以编译。
【解决方案2】:

这并不能真正解决在observeValueForKey 中使用switch 的问题,但它确实演示了我如何简化问题空间以消除冗长的代码。

我创建了一个实用程序类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)
    }
}

然后我的问题代码变成:

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

进行更改的主要原因是,维护所有不同的观察结果并正确添加和删除它们已成为一场噩梦。添加包装类消除了观察属性的问题和组以及当属性在同一位置更改时要采取的操作。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-02-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多