On Apr 17, 2017, at 05:40 , Jean-Daniel <mail...@xenonium.com> wrote:
> This is a good practice, but I don’t think this is required for computed > property, especially if you take care of willChange/didChange manually, as > the OP does. Here is what the Swift interoperability documentation says (https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/AdoptingCocoaDesignPatterns.html): > "You can use key-value observing with a Swift class, as long as the class > inherits from the NSObject class. You can use these three steps to implement > key-value observing in Swift. > > "1. Add the dynamic modifier to any property you want to observe. […]” Here is what the Swift language documentation says (https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html): > “dynamic” > > "Apply this modifier to any member of a class that can be represented by > Objective-C. When you mark a member declaration with the dynamic modifier, > access to that member is always dynamically dispatched using the Objective-C > runtime. Access to that member is never inlined or devirtualized by the > compiler.” That is, unless you specify “dynamic” there’s no *guarantee* that invocations to the property accessors will use obj_msgSend, and since there’s no way in Swift to guarantee that obj_msgSend *won’t* be used for the property, the outcome for automatic KVO is unpredictable. On Apr 17, 2017, at 08:07 , Charles Srstka <cocoa...@charlessoft.com> wrote: > > // Note that this doesn’t need to be dynamic, since we are not relying on > Cocoa’s built-in automatic swizzling, > // which is only needed if we are not calling willChangeValue(forKey:) and > didChangeValue(forKey:) ourselves. > @objc var version: String { > willSet { > // Send the willChange notification, if the value is different from > its old value. > if newValue != self.version { > self.willChangeValue(forKey: #keyPath(version)) > } > } > didSet { > // Send the didChange notification, if the value is different from its > old value. > if oldValue != self.version { > self.didChangeValue(forKey: #keyPath(version)) > } > } > } I tested what happens (in Swift 3.1, Xcode 8.3.1) using this code: > private var versionContext = 0 > > class ViewController: NSViewController { > @objc /*dynamic*/ var version: String = “” { > willSet { > if newValue != self.version { > self.willChangeValue (forKey: > #keyPath(version)) } > } > didSet { > if oldValue != self.version { > self.didChangeValue (forKey: #keyPath(version)) > } > } > } > override func viewDidLoad () { > super.viewDidLoad () > addObserver (self, forKeyPath: #keyPath(version), options: [], > context: &versionContext) > } > override func observeValue (forKeyPath keyPath: String?, of object: > Any?, change: [NSKeyValueChangeKey : Any]?, context: > UnsafeMutableRawPointer?) { > print ("observedValue for \(version)") > } > @IBAction func buttonClicked (_ sender: Any?) { // There’s a button in > the UI hooked up to this > version = version == "" ? "1" : "\(version)" > } > } This version of the code (with “dynamic” commented out) displays the observer message once, as desired, and then not again, as desired. Uncommenting “dynamic” causes the message to be displayed twice the first time, and then once more every subsequent button click. So, Charles’s approach *appears* to work, because the “version” property isn’t participating in automatic swizzling. However, it’s subtly wrong because there’s no way to prevent other source code from leading the compiler to *deduce* that the method is dynamic. Once that happens, there’s an extra unwanted notification every time the property is set. And again, in the converse scenario (automatic KVO, where you want notifications unconditionally) the “dynamic” keyword isn’t optional. The correct solution, I claim, is to replace the declaration of “version” with this: > static func automaticallyNotifiesObserversOfVersion () -> Bool { return > false } > @objc dynamic var version: String = “” { … } and then use either Charles’ or Jean-Daniel’s logic to generate the notifications manually as desired. (BTW, the “@objc” is currently redundant, but will soon become required, via SE-0160 <https://github.com/apple/swift-evolution/blob/master/proposals/0160-objc-inference.md>.) _______________________________________________ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com