> On Apr 19, 2017, at 9:12 PM, Quincey Morris 
> <quinceymor...@rivergatesoftware.com> wrote:
> 
> On Apr 19, 2017, at 15:49 , Charles Srstka <cocoa...@charlessoft.com> wrote:
>> 
>> Cocoa automagically does its secret subclass thing to wrap the setter and 
>> call the didChange/willChange calls for any property you didn’t tell it not 
>> to do. It needs the property to be dynamic for this to work. 
> 
> Yes, that’s almost exactly what I said**. But it still doesn’t make the 
> “dynamic” keyword an on/off switch. Again, the KVO notifications aren’t 
> activated *because* the method has the Swift-specific dynamic attribute, but 
> because the method uses Obj-C dispatching. The “dynamic” keyword [currently] 
> forces the method to use Obj-C dispatching, but the reverse isn’t true. In 
> the absence of the keyword, there’s nothing formally stopping the compiler 
> from using Obj-C dispatching if it chooses to.
> 
> At some level, though, I’m prepared to stipulate that it’s a distinction 
> without much practical difference.

The statement didn’t say anything about “dynamic” being an “on/off switch.” The 
statement that you were quibbling was that you can mark a stored property as 
‘dynamic’, and Cocoa will do the rest. It didn’t say anything about what 
*causes* it, it was meant to be a practical guide to how to implement KVO 
properties in Swift. And to make Cocoa automatically handle the 
didChange/willChange calls, you mark a property as dynamic. I mean, yes, we 
could go all the way down to explaining how everything works in terms of the 
physics and chemistry of electrons and semiconductors, but that wouldn’t be 
very practical, would it?

>> I should add that if a computed property needs ‘dynamic’ in order for its 
>> notifications to fire, the property is not properly KVO-compliant.
> 
> It’s impossible to say in general what counts as a change to a mutable 
> property. Indeed it’s perfectly possible for a property to exist for the 
> purpose of generating KVO notifications without having any meaningful value, 
> and there are plenty more un-generalizable considerations:

The entire purpose of KVO is to monitor changes to values. The word “value” is 
literally right there in the title, as well as most of the major APIs that make 
it work. observeValue. willChangeValue. didChangeValue. value(forKey:). 
setValue. keyPathsForValuesAffecting. Using KVO without a meaningful value is, 
frankly, using a hammer to drive in a screw. For a value-less notification, 
something like NSNotificationCenter is a much better choice.

> — It’s up to an individual property (with a meaningful value) to decide 
> whether KVO notifications are issued when the setter is called (in effect, 
> the default case) or when the value actually changes (as in Rick’s original 
> code), or under some other conditions (e.g. I can imagine a property limiting 
> the rate of notifications using a timer).

Which does not require a property to be dynamic, and in fact would require 
*disabling* Cocoa’s use of dynamism for the property, via returning false for 
automaticallyNotifiesObserversOf.

> — There’s no general presumption whether the value returned by the getter is 
> synchronized with the value passed to the setter.

The setter, however, is likely to make some kind of change to the underlying 
storage such that we will be notified of it. And if no such change is made, 
then there is little point in sending change notifications.

> — There’s no general presumption whether the value returned by the getter is 
> synchronized with the notification, in a multi-threaded environment.

Assuming you’re referring to the fact that something in another thread could 
change the property in the middle of your observer, this still has little that 
I can see to do with the ‘dynamic’ keyword.

> — There’s no general presumption that KVO notifications originate in the 
> property's setter at all, or in the setter only.
> 
> Etc. “keyPathsForValuesAffecting…”-style dependencies are just a convenient 
> short-cut to a specific, simple, typical behavior.

Who said there was?

The bottom line with KVO notifications is that you’ll get one when either:
        1. willChange and didChange get called for the property you’re 
watching, or
        2. willChange and didChange get called for something that the property 
you’re watching is dependent on.

In case 1, you can either call willChange or didChange yourself, or you can let 
Cocoa do it for you. In Swift, you need to mark it ‘dynamic’ for the latter to 
happen.

In case 2, you don’t need willChange or didChange to get called for your 
property, because we will already get notified when the dependency’s willChange 
and didChange get called. In fact, we might not want them to, because it’ll 
cause the notification to double-post—once in the setter, and then again in the 
dependency’s setter, as in the example below:

> import Foundation
> 
> class Foo: NSObject {
>       @objc dynamic var bar: String = ""
>       
>       @objc static let keyPathsForValuesAffectingBaz: Set<String> = 
> [#keyPath(bar)]
>       @objc dynamic var baz: String {
>               get { return self.bar }
>               set { self.bar = newValue }
>       }
>       
>       private var barContext = 0
>       private var bazContext = 0
>       
>       override init() {
>               super.init()
>               
>               self.addObserver(self, forKeyPath: #keyPath(bar), options: [], 
> context: &barContext)
>               self.addObserver(self, forKeyPath: #keyPath(baz), options: [], 
> context: &bazContext)
>       }
>       
>       override func observeValue(forKeyPath keyPath: String?, of object: 
> Any?, change: [NSKeyValueChangeKey : Any]?, context: 
> UnsafeMutableRawPointer?) {
>               if context == &barContext {
>                       print("bar changed: \(self.bar)")
>               } else if context == &bazContext {
>                       print("baz changed: \(self.baz)")
>               } else {
>                       super.observeValue(forKeyPath: keyPath, of: object, 
> change: change, context: context)
>               }
>       }
> }
> 
> let foo = Foo()
> foo.baz = "Baz"

When you run the code above, you get three notifications: one that baz changed, 
one that bar changed, and then another that baz changed. Without the ‘dynamic’ 
on baz, you’ll get its notifications only once when it’s set from Swift. Now, 
depending on how important it is to you to avoid these duplicates, you might 
want to add an automaticallyNotifiesObserversOfBaz to make sure this behavior 
doesn’t occur in *any* case. However, regardless of whether you want to go that 
far, it makes little sense to go out of one’s way to add something specifically 
to *enable* the double-posting behavior. Thus, there is no need for ‘dynamic’ 
on this property.

Charles

_______________________________________________

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

Reply via email to