> On Dec 15, 2016, at 2:51 PM, Michael Ilseman via swift-evolution 
> <[email protected]> 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.

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
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to