Re: Translating KVO-ed property to Swift
Re: Translating KVO-ed property to Swift
- Subject: Re: Translating KVO-ed property to Swift
- From: Quincey Morris <email@hidden>
- Date: Wed, 19 Apr 2017 19:12:36 -0700
On Apr 19, 2017, at 15:49 , Charles Srstka <email@hidden> 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: 0x000000010000190b TestKVO`ViewController.version.willset(newValue="1", self=0x0000618000100900) at ViewController.swift:16
frame #1: 0x0000000100001877 TestKVO`ViewController.version.setter(newValue="1", self=0x0000618000100900) at ViewController.swift:0
frame #2: 0x00000001000017d8 TestKVO`@objc ViewController.version.setter at ViewController.swift:0
frame #3: 0x00007fff91d6c897 Foundation`-[NSObject(NSKeyValueObservingPrivate) _changeValueForKeys:count:maybeOldValuesDict:usingBlock:] + 848
frame #4: 0x00007fff91bf1c7d Foundation`-[NSObject(NSKeyValueObservingPrivate) _changeValueForKey:key:key:usingBlock:] + 60
frame #5: 0x00007fff91c5a55b Foundation`_NSSetObjectValueAndNotify + 261
frame #6: 0x000000010000310f TestKVO`ViewController.buttonClicked(sender=some, self=0x0000618000100900) -> () at ViewController.swift:57
frame #7: 0x0000000100003200 TestKVO`@objc ViewController.buttonClicked(Any?) -> () at ViewController.swift:0
frame #8: 0x00007fffa5b903a7 libsystem_trace.dylib`_os_activity_initiate_impl + 53
frame #9: 0x00007fff8e475791 AppKit`-[NSApplication(NSResponder) sendAction:to:from:] + 456
and here is a disassembly of the Swift code (frame 6) that invokes the setter:
0x1000030f2 <+434>: movq 0x3ebf(%rip), %rsi ; "setVersion:"
0x1000030f9 <+441>: movq %rax, %rcx
0x1000030fc <+444>: movq -0x20(%rbp), %rdx
0x100003100 <+448>: movq %rdx, %rdi
0x100003103 <+451>: movq %rcx, %rdx
0x100003106 <+454>: movq %rax, -0x80(%rbp)
0x10000310a <+458>: callq 0x1000047d2 ; 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 (email@hidden)
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:
This email sent to email@hidden