Re: Translating KVO-ed property to Swift

2017-04-19 Thread Quincey Morris
On Apr 19, 2017, at 15:49 , Charles Srstka  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.

> 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:

— 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).

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

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

— 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.



** Incidentally, when I was testing in Swift, I didn’t see any indication the 
backtrace that a subclass was being used, or that any actual swizzling was 
being done. It looked more like the dynamic dispatch was diverted to a 
general-purpose function that generated the notifications around the actual 
setter invocation. Here’s a partial backtrace when a breakpoint in the 
“version” didSet accessor was hit:

frame #0: 0x0001190b 
TestKVO`ViewController.version.willset(newValue="1", self=0x618000100900) 
at ViewController.swift:16
frame #1: 0x00011877 
TestKVO`ViewController.version.setter(newValue="1", self=0x618000100900) at 
ViewController.swift:0
frame #2: 0x000117d8 TestKVO`@objc ViewController.version.setter at 
ViewController.swift:0
frame #3: 0x7fff91d6c897 
Foundation`-[NSObject(NSKeyValueObservingPrivate) 
_changeValueForKeys:count:maybeOldValuesDict:usingBlock:] + 848
frame #4: 0x7fff91bf1c7d 
Foundation`-[NSObject(NSKeyValueObservingPrivate) 
_changeValueForKey:key:key:usingBlock:] + 60
frame #5: 0x7fff91c5a55b Foundation`_NSSetObjectValueAndNotify + 261
frame #6: 0x0001310f 
TestKVO`ViewController.buttonClicked(sender=some, self=0x618000100900) -> 
() at ViewController.swift:57
frame #7: 0x00013200 TestKVO`@objc 
ViewController.buttonClicked(Any?) -> () at ViewController.swift:0
frame #8: 0x7fffa5b903a7 
libsystem_trace.dylib`_os_activity_initiate_impl + 53
frame #9: 0x7fff8e475791 AppKit`-[NSApplication(NSResponder) 
sendAction:to:from:] + 456

and here is a disassembly of the Swift code (frame 6) that invokes the setter:

0x130f2 <+434>: movq   0x3ebf(%rip), %rsi; "setVersion:"
0x130f9 <+441>: movq   %rax, %rcx
0x130fc <+444>: movq   -0x20(%rbp), %rdx
0x13100 <+448>: movq   %rdx, %rdi
0x13103 <+451>: movq   %rcx, %rdx
0x13106 <+454>: movq   %rax, -0x80(%rbp)
0x1310a <+458>: callq  0x147d2   ; symbol stub for: 
objc_msgSend

That is, the objc_msgSend ended up in the Foundation function 
_NSSetObjectValueAndNotify. I traced through the objc_msgSend, and it goes 
straight to _NSSetObjectValueAndNotify from the method dispatch tables.

Just an interesting sidelight to this discussion. In the old days, you could 
definitely see the secret subclass if you looked.

___

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 

Re: Translating KVO-ed property to Swift

2017-04-19 Thread Charles Srstka
> On Apr 19, 2017, at 4:50 PM, Quincey Morris 
>  wrote:
> 
>> 3. Computed properties do not need to be dynamic, […].
> 
> This is also not exactly true. Computed properties that have only a getter do 
> not need to be dynamic, because they don’t generate any KVO notifications via 
> a setter, and so “dynamic” is irrelevant.
> 
> However, a computed property that has a setter will *not* produce the 
> expected notifications unless it is marked “dynamic”, even if marked “@objc”. 
> (I tested this to be sure.) There’s nothing special about computed vs. stored 
> properties in this regard.

I should add that if a computed property needs ‘dynamic’ in order for its 
notifications to fire, the property is not properly KVO-compliant. There are 
three ways this could happen that I can think of off the top of my head:

Case #1: Derived from another KVO property

> @objc dynamic var foo: String
> 
> @objc var bar: String {
>   get { return self.foo }
>   set { self.foo = newValue }
> }

The above will not fire notifications for bar. Making bar ‘dynamic’ appears to 
fix the problem, but it is in fact still not compliant; if foo changes, bar’s 
value is changed as well, but its notifications will not fire. The proper way 
to fix this is to add keyPathsForValuesAffectingBar.

Case #2: Backed by something not exposed to Objective-C

> enum Option: String {
>   case foo = “Foo"
>   case bar = “Bar"
>   case baz = “Baz"
> }
> 
> var option: Option
> 
> @objc var objcOption: String {
>   get { return self.option.rawValue }
>   set {
>   if let option = Option(rawValue: newValue) {
>   self.option = option
>   }
>   }
> }

The above will not fire notifications for objcOption. Making objcOption 
‘dynamic’ appears to fix the problem, but if option is changed, objcOptions’s 
value will change as well, and its notifications will not fire. The proper way 
to fix this is to add willChangeValue(forKey:) and didChangeValue(forKey:) 
calls in option’s willSet and didSet accessors.

Case #3: It’s backed by something other than a property

> @objc var isFoo: Bool {
>   get { return UserDefaults.bool(forKey: “Foo”) }
>   set { UserDefaults.set(newValue, forKey: “Foo”) }
> }

The above will not fire notifications for isFoo. Making isFoo ‘dynamic’ appears 
to fix the problem, but if the “Foo” defaults key changes by some mechanism 
other than isFoo’s setter, isFoo’s value will have changed, but the 
notifications will not fire. The better solution is to register for changes on 
UserDefaults or NSUserDefaultsController via one of the several available 
mechanisms that Cocoa provides to do that and ensure that isFoo’s clients are 
notified when the underlying value changes.

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

Re: Translating KVO-ed property to Swift

2017-04-19 Thread Charles Srstka
> On Apr 19, 2017, at 4:50 PM, Quincey Morris 
>  wrote:
> 
> On Apr 19, 2017, at 10:56 , Charles Srstka  wrote:
>> 
>> 2. Stored properties need to call willChangeValue(forKey:) and 
>> didChangeValue(forKey:).
>>  a. In most cases, just add “dynamic” to the property declaration, and 
>> Cocoa will automagically insert the needed calls.
> 
> The problem with saying it this way is that it can be taken to imply that the 
> calls will be inserted statically *into the setter’s implementation*, which 
> is not true. It is true that Cocoa will automagically *execute* the needed 
> calls.
> 
> It also can be taken to imply that Cocoa does this automagically because the 
> property is dynamic, which is also not really true. Cocoa does this because 
> it’s a setter for an Obj-C property which returns true from its corresponding 
> automaticallyNotifiesObserversOfFoo class method. That is, “dynamic” doesn’t 
> turn the automagic behavior off or on, it’s simply a requirement to ensure 
> that the property is fully in the Obj-C domain.

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. Thus, if you add 
“dynamic” to the property declaration, Cocoa will do the rest. That’s really 
all that’s relevant from a usage standpoint.

>> 3. Computed properties do not need to be dynamic, […].
> 
> This is also not exactly true. Computed properties that have only a getter do 
> not need to be dynamic, because they don’t generate any KVO notifications via 
> a setter, and so “dynamic” is irrelevant.
> 
> However, a computed property that has a setter will *not* produce the 
> expected notifications unless it is marked “dynamic”, even if marked “@objc”. 
> (I tested this to be sure.) There’s nothing special about computed vs. stored 
> properties in this regard.
> 
> I think you were “really” talking about derived properties, which are 
> typically computed properties with only a getter, no setter.

Nope, you can have a setter. You just have to make sure you’ve properly set up 
keyPathsForValuesAffecting. Here is some test code you can run yourself 
and verify this:

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

When run, this outputs:

baz changed to Bar
baz changed to Baz

Exactly as you would expect.

> In addition, “dynamic” is documented as needed, in the place I linked to 
> before:
> 
>   
> https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/AdoptingCocoaDesignPatterns.html#//apple_ref/doc/uid/TP40014216-CH7-ID6
> 
> under the heading Key-Value Observing where it says: "Add the dynamic 
> modifier to any property you want to observe.” This may be overly 
> conservative, but it is at least official.
> 
> For all those reasons, I think it’s pointless to try to figure out the 
> contexts where “dynamic” is unnecessary. It seems to me that the best advice 
> is that from the documentation: unconditionally add “dynamic” to any property 
> you want to observe. (Well, in the future, add “@objc dynamic”.)

It’s an oversimplification. If you understand the reasons why the ‘dynamic’ 
keyword is necessary, you will also be able to identify where it serves solely 
as unnecessary overhead.

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

Re: Translating KVO-ed property to Swift

2017-04-19 Thread Quincey Morris
On Apr 19, 2017, at 10:56 , Charles Srstka  wrote:
> 
> 2. Stored properties need to call willChangeValue(forKey:) and 
> didChangeValue(forKey:).
>   a. In most cases, just add “dynamic” to the property declaration, and 
> Cocoa will automagically insert the needed calls.

The problem with saying it this way is that it can be taken to imply that the 
calls will be inserted statically *into the setter’s implementation*, which is 
not true. It is true that Cocoa will automagically *execute* the needed calls.

It also can be taken to imply that Cocoa does this automagically because the 
property is dynamic, which is also not really true. Cocoa does this because 
it’s a setter for an Obj-C property which returns true from its corresponding 
automaticallyNotifiesObserversOfFoo class method. That is, “dynamic” doesn’t 
turn the automagic behavior off or on, it’s simply a requirement to ensure that 
the property is fully in the Obj-C domain.

> 3. Computed properties do not need to be dynamic, […].

This is also not exactly true. Computed properties that have only a getter do 
not need to be dynamic, because they don’t generate any KVO notifications via a 
setter, and so “dynamic” is irrelevant.

However, a computed property that has a setter will *not* produce the expected 
notifications unless it is marked “dynamic”, even if marked “@objc”. (I tested 
this to be sure.) There’s nothing special about computed vs. stored properties 
in this regard.

I think you were “really” talking about derived properties, which are typically 
computed properties with only a getter, no setter.

In addition, “dynamic” is documented as needed, in the place I linked to before:


https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/AdoptingCocoaDesignPatterns.html#//apple_ref/doc/uid/TP40014216-CH7-ID6
 


under the heading Key-Value Observing where it says: "Add the dynamic modifier 
to any property you want to observe.” This may be overly conservative, but it 
is at least official.

For all those reasons, I think it’s pointless to try to figure out the contexts 
where “dynamic” is unnecessary. It seems to me that the best advice is that 
from the documentation: unconditionally add “dynamic” to any property you want 
to observe. (Well, in the future, add “@objc dynamic”.)


___

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

Re: Translating KVO-ed property to Swift

2017-04-19 Thread Charles Srstka
> On Apr 17, 2017, at 7:40 AM, Jean-Daniel  wrote:
> 
>> Le 17 avr. 2017 à 10:52, Quincey Morris > > a écrit :
>> 
>> On Apr 17, 2017, at 01:43 , Jean-Daniel >  > >> wrote:
>>> 
>>> var version: String? {
>> 
>> A slight correction: this declaration actually must be:
>> 
>>> dynamic var version: String? {
>> 
>> 
>> otherwise KVO isn’t guaranteed to work in Swift.
> 
> 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’s the set of rules I recommend for writing KVO-compliant properties in 
Swift:

1. Always put @objc in front of every declaration involved, whether it’s the 
property itself or the static properties that are there to support it.

2. Stored properties need to call willChangeValue(forKey:) and 
didChangeValue(forKey:).
a. In most cases, just add “dynamic” to the property declaration, and 
Cocoa will automagically insert the needed calls.
b. If you call willChangeValue and didChangeValue manually, add the 
following static property, changing Foo to the property’s name. Since this is 
easy to misspell accidentally, it’s best to make an Xcode code snippet for it.

@objc private static let automaticallyNotifiesObserversOfFoo = false

3. Computed properties do not need to be dynamic, but should register their 
dependencies. The properties that form these dependencies also need to be 
KVO-compliant. To register dependencies, add the following static property. 
This is also helpful to set as an Xcode snippet to avoid typos.

@objc private static let keyPathsForValuesAffectingFoo: Set = 
[#keyPath(bar)]

The snippet above tells Cocoa that the KVO-compliant property “foo” depends on 
the KVO-compliant property “bar”, meaning that if “bar” changes, “foo”’s 
observers will be notified as well.

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

Re: Sierra Document Handling

2017-04-19 Thread Richard Charles

> On Apr 11, 2017, at 12:41 PM, Richard Charles  wrote:
> 
> Filed bug report 31560971 SharedFileList Debug Logging.

I was pleasantly surprised to hear from Apple regarding this issue.

Response from Apple: Duplicate of 30513407 (Open).

So apparently this is a legitimate issue.

--Richard 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