Swift 中带有 KVC 属性 的 NSView

NSView with a KVC property in Swift

我有一个自定义 NSView class 定义为:

class MyView: NSView
{
    var someText: NSString

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

        super.didChangeValueForKey( key )
    }

    // other stuff
}

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

我该怎么做?

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

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

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

但是,如果您不需要所有花里胡哨的东西哦,KVC,didSet 是正确的选择。

更新

至于didChangeValueForKey:——它的目的恰恰相反,通知你键的值已经改变(如果不是由于基金会涵盖的情况之一)。您应该使用 addObserver(_:forKeyPath:options:context:) 并覆盖 observeValueForKeyPath(_:ofObject:change:context:) 以获得更改通知。

或者,您可以使用许多第 3 方解决方案之一,例如 ReactiveCococa or Facebook's KVOController

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"

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

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

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

didChangeValueForKey() 不是 KVC 的一部分,它是 KVO(键值观察)的一部分。它不打算被覆盖。它旨在在实施手动更改通知时调用(与 willChangeValueForKey() 成对)。

不过,更重要的是,没有理由相信它会在 属性 没有被任何东西观察到的情况下被调用。 KVO swizzles class 以便挂接到 setters 和其他变异访问器 以获取实际被观察到的那些属性 。当这样一个 属性 被改变时(并且支持自动改变通知),KVO 自动调用 willChangeValueForKey()didChangeValueForKey()。但是对于非观察属性,不会调用那些方法。

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

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