Re: Translating KVO-ed property to Swift
Re: Translating KVO-ed property to Swift
- Subject: Re: Translating KVO-ed property to Swift
- From: Charles Srstka <email@hidden>
- Date: Wed, 19 Apr 2017 17:49:16 -0500
> On Apr 19, 2017, at 4:50 PM, Quincey Morris <email@hidden> wrote:
>
> On Apr 19, 2017, at 10:56 , Charles Srstka <email@hidden> 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<key>. 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<String> = [#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: &kvoContext)
> }
>
> override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
> if context == &kvoContext {
> 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 (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