【问题标题】:NSView with a KVC property in Swift在 Swift 中具有 KVC 属性的 NSView
【发布时间】:2015-08-30 18:49:23
【问题描述】:

我有一个自定义的 NSView 类定义为:

class MyView: NSView
{
    var someText: NSString

    override func didChangeValueForKey(key: String)
    {
        println( key )

        super.didChangeValueForKey( key )
    }

    // other stuff
}

我想要做的是从这个类之外更改 someText 的值,并让 didChangeValueForKey 注意到 someText 已更改,因此我可以将视图的 needsDisplay 设置为 true 并做一些其他工作。

我是怎么做到的?

【问题讨论】:

    标签: xcode swift cocoa kvc


    【解决方案1】:

    您确定为此需要 KVC 吗? KVC 在 Swift 中运行良好,但有一种更简单的方法:

    var SomeText: NSString {
        didSet {
            // do some work every time SomeText is set
        }
    }
    

    【讨论】:

      【解决方案2】:

      没有用于此的 KVC 机制,因为这不是 KVC 的用途。

      在 Objective-C 中,您将显式实现 setter(如果属性最初来自超类,则覆盖)并在那里完成您的工作。

      在 Swift 中,正确的方法是 didSet 机制。

      didChangeValueForKey() 不是 KVC 的一部分,它是 KVO(Key-Value Observing)的一部分。它不打算被覆盖。它旨在在实施手动更改通知时调用(与willChangeValueForKey() 配对)。

      不过,更重要的是,没有理由相信它会被任何东西都没有观察到的属性调用。 KVO 对类进行 swizzle,以便挂钩到设置器和其他可变访问器用于实际观察到的那些属性。当这样的属性发生变化时(并且支持自动变化通知),KVO 会自动调用willChangeValueForKey()didChangeValueForKey()。但是对于未观察到的属性,这些方法不会被调用。

      最后,在某些情况下,例如索引集合变异访问器,KVO 会使用不同的更改通知方式,例如willChange(_:valuesAtIndexes:forKey:)didChange(_:valuesAtIndexes:forKey:)

      如果出于某种原因你真的不想使用didSet,你可以使用KVO 来观察selfsomeText 属性的变化并处理observeValueForKeyPath(_:ofObject:change:context:) 的变化。但这是一种糟糕、笨拙、容易出错、效率低下的简单方法。

      【讨论】:

        【解决方案3】:

        KVO 和didSet 不互斥:

        import Foundation
        class C: NSObject {
            dynamic var someText: String = "" {
                didSet {
                    print("changed to \(someText)")
                }
            }
        }
        
        let c = C()
        c.someText = "hi"  // prints "changed to hi"
        
        class Observer: NSObject {
            init(_ c: C) {
                super.init()
                c.addObserver(self, forKeyPath: "someText", options: [], context: nil)
            }
        
            override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
                print("observed change to \(object!.valueForKeyPath(keyPath!))")
            }
        }
        
        let o = Observer(c)
        c.someText = "test"  // prints "changed to test" and "observed change to test"
        

        【讨论】:

          【解决方案4】:

          我想在Jaanus 的回答中添加,要使属性 KVC 兼容,您应该将其声明为 dynamic var someText: NSString

          但如果您不需要所有的花里胡哨哦 KVC,didSet 是您的最佳选择。

          更新

          至于didChangeValueForKey: – 它的目的是相反的,让您通知键的值已更改(如果不是由于 Foundation 涵盖的情况之一)。您应该使用 addObserver(_:forKeyPath:options:context:) 并覆盖 observeValueForKeyPath(_:ofObject:change:context:) 以收到更改通知。

          或者,您可以使用许多第三方解决方案之一,例如 ReactiveCococa 或 Facebook 的 KVOController

          【讨论】:

          • 即使我将动态参数添加到 someText,也不会调用 didChangeValueForKey 方法。如果 didChangeValueForKey 不是知道 someText 的值发生变化的正确方法,那是什么?
          • 抱歉没有注意到那部分,这确实不是正确的方法,请看我的编辑
          猜你喜欢
          • 1970-01-01
          • 2023-03-11
          • 1970-01-01
          • 2016-10-23
          • 2012-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多