On Thu, Dec 15, 2016, at 02:29 PM, Charles Srstka wrote:
> > On Dec 15, 2016, at 2:51 PM, Michael Ilseman via swift-evolution 
> > <swift-evolution@swift.org> wrote:
> > 
> >> I don't think we should ever make it possible to mark an entire class as 
> >> `dynamic`. This just reintroduces the Obj-C problem, where many properties 
> >> support KVO, but not all, and there's no indication on the property itself 
> >> as to whether it supports it.
> >> 
> > 
> > I’m not familiar enough with these kinds of bugs. Kevin, do you think the 
> > existing behavior aligns with or runs counter to safe-by-default?
> 
> The problem Kevin describes is still here; ‘dynamic’ itself is quite 
> orthogonal to KVO as a concept. A method being declared ‘dynamic’ is no 
> guarantee that it actually supports KVO, and likewise, a method does not need 
> to be marked ‘dynamic’ in order to support KVO. You can, for example, either 
> call willChangeValue() and didChangeValue() in your willSet and didSet 
> accessors for a stored property, or for a computed property you can simply 
> implement the proper accessor methods to describe the dependencies, as in the 
> example below.
> 
> import Foundation
> 
> class Watcher: NSObject {
>       var kvoContext = 0
>       
>       init(watched: Watched) {
>               super.init()
>               watched.addObserver(self, forKeyPath: "foo", options: [], 
> context: &kvoContext)
>       }
>       
>       override func observeValue(forKeyPath keyPath: String?, of object: 
> Any?, change: [NSKeyValueChangeKey : Any]?, context: 
> UnsafeMutableRawPointer?) {
>               if context == &kvoContext {
>                       print("foo changed; now it's \((object as! 
> Watched).foo)")
>               } else {
>                       super.observeValue(forKeyPath: keyPath, of: object, 
> change: change, context: context)
>               }
>       }
> }
> 
> class Watched: NSObject {
>       // not a single ‘dynamic’ member in here:
> 
>       class func keyPathsForValuesAffectingFoo() -> Set<String> { return 
> ["bar"] }
>       var foo: Int {
>               get { return self.bar }
>               set(foo) { self.bar = foo }
>       }
>       
>       var bar: Int = 0 {
>               willSet { self.willChangeValue(forKey: "bar") }
>               didSet { self.didChangeValue(forKey: "bar") }
>       }
> }
> 
> let watched = Watched()
> let watcher = Watcher(watched: watched)
> 
> watched.bar = 5 // outputs: "foo changed; now it's 5”
> 
> - - - - - -
> 
> All ‘dynamic’ does for you vis a vis KVO conformance is to automatically add 
> the willChangeValue() and didChangeValue() calls, *for a stored property.* 
> For a computed property, that doesn’t help you, if the dependencies aren’t 
> properly set up. For example, if we replace the Watched class with:
> 
> class Watched: NSObject {
>       dynamic var foo: Int {
>               get { return self.bar }
>               set(foo) { self.bar = foo }
>       }
>       
>       dynamic var bar: Int = 0 {
>               willSet { self.willChangeValue(forKey: "bar") }
>               didSet { self.didChangeValue(forKey: "bar") }
>       }
> }
> 
> - - - - - -
> 
> and then change the “bar” property, no notifications will be fired, even 
> though the value of ‘foo’ has indeed changed. So to a client of the class who 
> can only see its interface and not the source code, ‘dynamic’ is useless on 
> its own for determining KVO conformance.

The problem with that code isn't that `dynamic` doesn't work for computed 
properties. It does; if you mutate the `foo` property, you'll get the KVO 
notifications. The problem is you have one property that depends on another and 
you didn't set up the KVO machinery properly using 
automaticallyNotifiesObservers(forKey:) or 
automaticallyNotifiesObserversOf<key>() (incidentally in Swift you can write 
the latter as a `static let`, since that becomes a class method in Obj-C).

So yes, `dynamic` by itself doesn't mean that the property supports KVO. But 
there are very few reasons to use `dynamic` outside of supporting KVO, so it's 
a pretty good signal that the property does support it. And conversely, not 
having `dynamic` doesn't mean that it doesn't support KVO, though if it does 
have manual KVO support using will/didChangeValue(forKey:) then it should be 
documented as such.

> Bottom line: we really do need a new KVO-replacement system for Swift, one 
> that could actually advertise itself as part of the interface instead of 
> relying on documentation (especially since Apple isn’t interested in writing 
> it anymore; the number of classes and methods that just say “No overview 
> available.” in the documentation viewer in Sierra is really quite 
> astounding). Unfortunately, this is probably additive, and thus probably 
> won’t be heard until the next phase of swift-evolution.
> 
> Charles
> 
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to