On Thu, Dec 15, 2016, at 02:33 PM, Kevin Ballard wrote: > 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).
Oops, I mean keyPathsForValuesAffectingValue(forKey:) and keyPathsForValuesAffecting<Key>(). That said, your code as written actually sends 2 KVO change notices for "bar", and you do still need to implement automaticallyNotifiesObservers… to disable the automatic KVO notice for "bar". -Kevin Ballard > 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