Discussion:
Translating KVO-ed property to Swift
Rick Mann
2017-04-17 08:24:14 UTC
Permalink
I have a number of properties in Objective-C written like this, short-circuiting notifications when the value doesn't change:

-------------
@synthesize version = mVersion

- (void)
setVersion: (NSString *) inVersion
{
if (inVersion == nil && mVersion == nil)
{
return;
}
if ([inVersion isEqualToString: mVersion])
{
return;
}

[self willChangeValueForKey: @"version"];
mVersion = inVersion;
[self didChangeValueForKey: @"version"];
}
-------------

Now I want to translate this method into Swift. Thing is, AFAIK you can't name the ivar created for a property. Is there a way to translate this to swift?

TIA,
--
Rick Mann
***@latencyzero.com


_______________________________________________

Cocoa-dev mailing list (Cocoa-***@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/gegs%40ml-in.narkive.net

This email sent to ***@ml-in.narkive.net
Jean-Daniel
2017-04-17 08:43:10 UTC
Permalink
One way to solve that is to declare an explicit private stored property for the ivar, and a public computed property for the logic.

private var _version: String? = nil

var version: String? {
get { return _version }
set { your set version code }
}
Post by Rick Mann
-------------
@synthesize version = mVersion
- (void)
setVersion: (NSString *) inVersion
{
if (inVersion == nil && mVersion == nil)
{
return;
}
if ([inVersion isEqualToString: mVersion])
{
return;
}
mVersion = inVersion;
}
-------------
Now I want to translate this method into Swift. Thing is, AFAIK you can't name the ivar created for a property. Is there a way to translate this to swift?
TIA,
--
Rick Mann
_______________________________________________
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
https://lists.apple.com/mailman/options/cocoa-dev/mailing%40xenonium.com
_______________________________________________

Cocoa-dev mailing list (Cocoa-***@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/gegs%40ml-in.narkive.net

This email sent to ***@ml-in.na
Quincey Morris
2017-04-17 08:52:27 UTC
Permalink
Post by Jean-Daniel
var version: String? {
dynamic var version: String? {
otherwise KVO isn’t guaranteed to work in Swift.

_______________________________________________

Cocoa-dev mailing list (Cocoa-***@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/gegs%40ml-in.narkive.net

This email sent to ***@ml-in.na
Jean-Daniel
2017-04-17 12:40:35 UTC
Permalink
Post by Quincey Morris
Post by Jean-Daniel
var version: String? {
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.


_______________________________________________

Cocoa-dev mailing list (Cocoa-***@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/gegs%40ml-in.narkive.net

This emai
Charles Srstka
2017-04-19 17:56:55 UTC
Permalink
Post by Jean-Daniel
Post by Quincey Morris
Post by Jean-Daniel
var version: String? {
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<String> = [#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-***@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/gegs%40ml-in.narkive.net

This e
Quincey Morris
2017-04-19 21:50:31 UTC
Permalink
Post by Charles Srstka
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.
Post by Charles Srstka
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 <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-***@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/gegs%40ml-in.narkive.net

Th
Charles Srstka
2017-04-19 22:49:16 UTC
Permalink
Post by Quincey Morris
Post by Charles Srstka
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.
Post by Quincey Morris
Post by Charles Srstka
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.
I think you were “really” talking about derived properties, which are typically computed properties with only a getter, no setter.
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.
Post by Quincey Morris
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.
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-***@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/gegs%40ml-in.narkive.net

This email s
Quincey Morris
2017-04-20 02:12:36 UTC
Permalink
Post by Charles Srstka
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.
Post by Charles Srstka
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 (Cocoa-***@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/gegs%40ml-in.narkive.net

This email sent to gegs
Charles Srstka
2017-04-20 17:24:07 UTC
Permalink
Post by Quincey Morris
Post by Charles Srstka
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?
Post by Quincey Morris
Post by Charles Srstka
I should add that if a computed property needs ‘dynamic’ in order for its notifications to fire, the property is not properly KVO-compliant.
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.
Post by Quincey Morris
— 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.
Post by Quincey Morris
— 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.
Post by Quincey Morris
— 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.
Post by Quincey Morris
— 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.
Post by Quincey Morris
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-***@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/gegs%40ml-in.narkive.net
Quincey Morris
2017-04-20 20:06:53 UTC
Permalink
Post by Charles Srstka
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 subscribe to the principle already quoted from the documentation: If you KVO-observe a property, mark it “dynamic”. I like this because it is simple and unambiguous. If anyone asked me for advice, I’d say to follow this principle and don’t agonize any further.

As a different discussion, at a different level, I agree that there are scenarios where you can omit “dynamic” and still get all the KVO notifications you wanted. What scenarios? Well, I think the essence of your argument is that it can be omitted for a KVO-observed *dependent* property. That at least sounds pretty plausible to me, and it may even be true by definition. If anyone asked you for advice, then I guess you’d say to accept this as an extension of the documented principle. Me, I’m not 100% certain about this, if for no other reason than the documented principle is future-proof against a change in the way things work where Apple says, “Well, we told to you mark all KVO-observed properties dynamic.” But, people can go for advice where they want.

Where I disagree is in your much stronger claim that a computed property is *necessarily* a dependent property. I think this is just false. The Swift distinction between computed and stored properties is syntactic and has nothing to do with KVO**. A KVO-observed computed property should therefore, according to the documented principle, be marked “dynamic”. For anyone following your extended principle, if it’s also a dependent property, they can omit the “dynamic”. If not, not.


** Indeed, it’s trivially easy to convert a public stored property into a public computed property that uses a private, stored, non-KVO-observed property as its instance storage.

_______________________________________________

Cocoa-dev mailing list (Cocoa-***@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/gegs%40ml-in.narkive.net

This email sent to ***@ml-in.narkive
Jean-Daniel
2017-04-20 21:12:30 UTC
Permalink
Post by Quincey Morris
Post by Charles Srstka
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 subscribe to the principle already quoted from the documentation: If you KVO-observe a property, mark it “dynamic”. I like this because it is simple and unambiguous. If anyone asked me for advice, I’d say to follow this principle and don’t agonize any further.
As a different discussion, at a different level, I agree that there are scenarios where you can omit “dynamic” and still get all the KVO notifications you wanted. What scenarios? Well, I think the essence of your argument is that it can be omitted for a KVO-observed *dependent* property. That at least sounds pretty plausible to me, and it may even be true by definition. If anyone asked you for advice, then I guess you’d say to accept this as an extension of the documented principle. Me, I’m not 100% certain about this, if for no other reason than the documented principle is future-proof against a change in the way things work where Apple says, “Well, we told to you mark all KVO-observed properties dynamic.” But, people can go for advice where they want.
Where I disagree is in your much stronger claim that a computed property is *necessarily* a dependent property. I think this is just false. The Swift distinction between computed and stored properties is syntactic and has nothing to do with KVO**. A KVO-observed computed property should therefore, according to the documented principle, be marked “dynamic”. For anyone following your extended principle, if it’s also a dependent property, they can omit the “dynamic”. If not, not.
** Indeed, it’s trivially easy to convert a public stored property into a public computed property that uses a private, stored, non-KVO-observed property as its instance storage.
_______________________________________________
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
https://lists.apple.com/mailman/options/cocoa-dev/mailing%40xenonium.com
_______________________________________________

Cocoa-dev mailing list (Cocoa-***@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/gegs%40ml-in.narkive.net

This
Charles Srstka
2017-04-23 17:27:58 UTC
Permalink
Sorry I didn’t get around to replying sooner; I’ve had a busy past few days.
Post by Quincey Morris
Post by Charles Srstka
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 subscribe to the principle already quoted from the documentation: If you KVO-observe a property, mark it “dynamic”. I like this because it is simple and unambiguous. If anyone asked me for advice, I’d say to follow this principle and don’t agonize any further.
It is simple, and it’ll work, and there’s certainly nothing wrong with it. It’s a little non-ideal performance-wise, though, which is why I prefer using dynamic only where it’s needed.
Post by Quincey Morris
As a different discussion, at a different level, I agree that there are scenarios where you can omit “dynamic” and still get all the KVO notifications you wanted. What scenarios? Well, I think the essence of your argument is that it can be omitted for a KVO-observed *dependent* property.
The essence of my argument is pretty simple; it can be omitted for any property where willChange and didChange are already being called.

For a dependent property, those calls will be made in your dependency, so you don’t need dynamic. For a stored property where you’re calling willChange and didChange yourself, you don’t need dynamic. For, I dunno, weird situations where willChange and didChange are getting called somewhere else but at the appropriate times, you don’t need dynamic. Dynamic is only needed to let Cocoa add the willChange and didChange notifications for you.
Post by Quincey Morris
That at least sounds pretty plausible to me, and it may even be true by definition. If anyone asked you for advice, then I guess you’d say to accept this as an extension of the documented principle. Me, I’m not 100% certain about this, if for no other reason than the documented principle is future-proof against a change in the way things work where Apple says, “Well, we told to you mark all KVO-observed properties dynamic.” But, people can go for advice where they want.
This isn’t just based on empirical observation, though; it’s based on the documentation of the KVO mechanism, which predates Swift by quite a lot. KVO isn’t a mysterious black box; the details of it are all available in the documentation, and once you understand them, you understand *why* dynamic is needed. And once you understand *why* it’s needed, you also will understand *where* it’s needed. Personally, I prefer to, when possible, understand the reasons behind doing things rather than repeating magical incantations.

As for future-proof, I fully expect that in some future version of Swift (Swift 5, hopefully, maybe Swift 6), we’ll get a new and improved, Swift-native replacement for KVC and KVO and we’ll all be ripping out our existing KVO code in favor of that anyway.
Post by Quincey Morris
Where I disagree is in your much stronger claim that a computed property is *necessarily* a dependent property. I think this is just false. The Swift distinction between computed and stored properties is syntactic and has nothing to do with KVO**. A KVO-observed computed property should therefore, according to the documented principle, be marked “dynamic”. For anyone following your extended principle, if it’s also a dependent property, they can omit the “dynamic”. If not, not.
Do you have any example of a property not backed by anything where KVO even makes sense? The whole purpose of KVO is to watch changes to values, and if there’s no value, it seems like the wrong tool.

I mean, sure, I could make this observable:

var whyWouldIObserveThis: Int {
return Int(arc4random())
}

and then send didChange/willChange notifications on a timer or something. But... *why?*

Charles

_______________________________________________

Cocoa-dev mailing list (Cocoa-***@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/gegs%40ml-in.narkive.net

Th
Richard Charles
2017-04-24 15:07:59 UTC
Permalink
Post by Charles Srstka
Post by Quincey Morris
Where I disagree is in your much stronger claim that a computed property is *necessarily* a dependent property. I think this is just false. The Swift distinction between computed and stored properties is syntactic and has nothing to do with KVO**. A KVO-observed computed property should therefore, according to the documented principle, be marked “dynamic”. For anyone following your extended principle, if it’s also a dependent property, they can omit the “dynamic”. If not, not.
Do you have any example of a property not backed by anything where KVO even makes sense? The whole purpose of KVO is to watch changes to values, and if there’s no value, it seems like the wrong tool.
I have a property that returns an array. A setter for this property makes no sense. The property returns a collection of managed objects that is the composite value of several to-many relationships.

This property is KVO compliant and bound to the NSContentArrayBinding of an array controller.

There is most likely more than one way to do this but I find using bindings convienient. You can bind to a KVO compliant property. You can't bind to a notification.

This is in an Objective-C project that requires interoperability with a C++ library. So using Swift is not possible but maybe someday it will be.

--Richard Charles

_______________________________________________

Cocoa-dev mailing list (Cocoa-***@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/gegs%40ml-in.narkive.net

This email sent to
Charles Srstka
2017-04-24 17:11:03 UTC
Permalink
Post by Richard Charles
Post by Charles Srstka
Post by Quincey Morris
Where I disagree is in your much stronger claim that a computed property is *necessarily* a dependent property. I think this is just false. The Swift distinction between computed and stored properties is syntactic and has nothing to do with KVO**. A KVO-observed computed property should therefore, according to the documented principle, be marked “dynamic”. For anyone following your extended principle, if it’s also a dependent property, they can omit the “dynamic”. If not, not.
Do you have any example of a property not backed by anything where KVO even makes sense? The whole purpose of KVO is to watch changes to values, and if there’s no value, it seems like the wrong tool.
I have a property that returns an array. A setter for this property makes no sense. The property returns a collection of managed objects that is the composite value of several to-many relationships.
This property is KVO compliant and bound to the NSContentArrayBinding of an array controller.
There is most likely more than one way to do this but I find using bindings convienient. You can bind to a KVO compliant property. You can't bind to a notification.
This is in an Objective-C project that requires interoperability with a C++ library. So using Swift is not possible but maybe someday it will be.
--Richard Charles
But it sounds like this property *is* backed by a value. The backing is the collection of managed objects. I take it you’re simply watching changes to the C++ source data and calling didChange/willChange when appropriate—basically translating one type of notification to another. This makes perfect sense—although not having a setter, being marked ‘dynamic’ would not change its behavior at all.

What Quincey seemed to be referring to was a property that was not backed by any kind of persistent value. Presumably, this property would also have a setter, since otherwise ‘dynamic’ isn’t really in play. I’m very curious as to what the applicability of such a thing would be, because I can’t think of any myself.

Charles

_______________________________________________

Cocoa-dev mailing list (Cocoa-***@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/gegs%40ml-in.narkive.n
Quincey Morris
2017-04-24 18:17:18 UTC
Permalink
Post by Charles Srstka
What Quincey seemed to be referring to was a property that was not backed by any kind of persistent value.
That wasn’t actually the kind of property I had in mind. I was thinking of settable, computed properties that did not depend on the value(s) of other KVO-observable properties. There probably aren’t many plausible *simple* examples, but I was thinking of such cases as when the value is stored in an I/O device rather than RAM, or when the value is retrieved from a server.

One problem with such examples, though, is that it’s not clear in the abstract whether you would keep a stored (RAM) value as well. Another problem I’ve been brooding over is whether a property (stored or computed) that has KVO dependencies can actually be regarded as a dependent property *if it has a setter*. Depending on the details, I suspect, the property could be regarded as fully dependent, partially dependent, not dependent, or not classifiable.

Separately, I’ve been brooding over the kinds of audiences that might need advice (advisories) about using “dynamic”. I think there’s a large fraction of the developer population, perhaps a majority, who aren’t aware of the details of how KVO notifications are implemented, and an explanation is just a distraction that’s more confusing than enlightening. (For any one developer, that can change over time, of course.)

This makes me think that there need to be two rules, one simple and one advanced, for two different audiences.
Post by Charles Srstka
Add the “dynamic” keyword if the property has a setter.
This covers the cases where “dynamic” is absolutely required (independent properties), and it covers all dependent properties without a setter (which, in practice, is most of them). It *might* add an unnecessary “dynamic” to a computed property with a setter, but I really think it’s the safest solution in such cases.
Post by Charles Srstka
For any property with a setter, if you generate notifications manually via willSet/didSet, and you don’t need or want automatic notifications produced by the setter, you must implement “automaticallyNotifiesObserversOf<Key>” to return ‘false’, and the property doesn’t need the “dynamic” keyword. Otherwise, add “dynamic” if there is a setter.
That is (I hope/intend) exactly equivalent to what Charles has being saying.

_______________________________________________

Cocoa-dev mailing list (Cocoa-***@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/gegs%40ml-in.narkive.net

This email sent to ***@ml-in.narkive.n
Charles Srstka
2017-04-24 23:22:10 UTC
Permalink
Post by Quincey Morris
Post by Charles Srstka
What Quincey seemed to be referring to was a property that was not backed by any kind of persistent value.
That wasn’t actually the kind of property I had in mind. I was thinking of settable, computed properties that did not depend on the value(s) of other KVO-observable properties. There probably aren’t many plausible *simple* examples, but I was thinking of such cases as when the value is stored in an I/O device rather than RAM, or when the value is retrieved from a server.
In this case, I would try to see if I could find some way to monitor changes in the underlying storage, and call the willChange and didChange methods manually when changes occur. I’d only make the property dynamic if it was completely impossible to do this, and I’d keep this to a last resort. The reason is because in this case, the property is not fully KVO-compliant, because its value can change without notifications being sent.

I did kind of touch on this with the UserDefaults-example I provided a while back. Pretending that NSUserDefaults still notified via NSNotifications only, as was the case before native KVO support was added to it, you would register for the NSNotification, check whether your default had changed, and fire the willSet and didSet notifications once it did. This is a lot more robust than depending on the automatic notifications when your property’s value may change without going through the property’s setter.

Charles

_______________________________________________

Cocoa-dev mailing list (Cocoa-***@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/gegs%40ml-in.narkive.net

This email sent to ***@ml-in.n

Charles Srstka
2017-04-19 23:21:40 UTC
Permalink
Post by Quincey Morris
Post by Charles Srstka
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.
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
Post by Quincey Morris
@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
Post by Quincey Morris
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
Post by Quincey Morris
@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-***@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/gegs%40ml-in.narkive.net

This email sent to gegs
Quincey Morris
2017-04-17 08:45:03 UTC
Permalink
Not in this code you don’t, unless you have a “automaticallyNotifiesObserversOfVersion” method returning false elsewhere in the class. The notification doesn’t happen in the synthesized implementation of the setter, but via a swizzled replacement setter that is normally installed only when something starts observing the property. Anyway …
Post by Rick Mann
Now I want to translate this method into Swift. Thing is, AFAIK you can't name the ivar created for a property. Is there a way to translate this to swift?
You do it in the “obvious” way — you declare a second, private property without a custom getter or setter, and you use this private property where you would have used the instance variable in Obj-C. The effect is the same, because the private property is optimized into just an instance variable, and the public property has no instance storage of its own (but you will have to provide a custom getter, too).

_______________________________________________

Cocoa-dev mailing list (Cocoa-***@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/gegs%40ml-in.narkive.net

Thi
Charles Srstka
2017-04-17 15:07:55 UTC
Permalink
Post by Rick Mann
-------------
@synthesize version = mVersion
- (void)
setVersion: (NSString *) inVersion
{
if (inVersion == nil && mVersion == nil)
{
return;
}
if ([inVersion isEqualToString: mVersion])
{
return;
}
mVersion = inVersion;
}
-------------
Now I want to translate this method into Swift. Thing is, AFAIK you can't name the ivar created for a property. Is there a way to translate this to swift?
I’ve been converting a lot of KVO properties to Swift lately. Here’s how I’d do this:

// Only needed if the property will be accessed by Objective-C code, since Swift code won’t see the swizzled accessors anyway for a non-dynamic property.
// @objc annotation is needed on every method we write here, since otherwise it’ll quit working when @objc inference is removed in Swift 4 (SE-0160)
@objc private static let automaticallyNotifiesObserversOfVersion: Bool = false

// Our actual version property. If you want, you can create a private property named “mVersion” and it will function like an instance variable.
// But I’d probably just use willSet and didSet.
// 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))
}
}
}

Charles

_______________________________________________

Cocoa-dev mailing list (Cocoa-***@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/gegs%40ml-in.narkive.net

This e
Quincey Morris
2017-04-17 18:09:44 UTC
Permalink
Post by Jean-Daniel
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.
"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. […]”
“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.
Post by Jean-Daniel
// 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))
}
}
}
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.
Post by Jean-Daniel
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-***@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/gegs%40ml-in.narkive.net

This email sent to ***@ml-i
Charles Srstka
2017-04-17 19:03:36 UTC
Permalink
Post by Quincey Morris
Post by Jean-Daniel
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.
And the reason for this is because Cocoa swizzles the accessor and adds the willChangeValue() and didChangeValue() calls. If you’re calling these yourself (or if you’re a computed property that registers its dependencies via keyPathsForValuesAffecting<property> methods), you don’t need dynamic.
Post by Quincey Morris
Post by Jean-Daniel
"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. […]”
“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.
Post by Jean-Daniel
// 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))
}
}
}
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.
“This approach *appears* to work, but it stops working if I change something that was deliberately set the way it was so that it would work.”
Post by Quincey Morris
import Foundation
class Foo: NSObject {
// Only needed if the property will be accessed by Objective-C code, since Swift code won’t see the swizzled accessors anyway for a non-dynamic property.
@objc private static let automaticallyNotifiesObserversOfVersion: Bool = false
// Our actual version property. If you want, you can create a private property named “mVersion” and it will function like an instance variable.
// But I’d probably just use willSet and didSet.
// 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 dynamic 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))
}
}
}
private var kvoContext = 0
override init() {
self.version = "4K78"
super.init()
self.addObserver(self, forKeyPath: #keyPath(version), options: [], context: &kvoContext)
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if context == &kvoContext {
print("version is now \(self.version)")
} else {
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
}
}
}
let foo = Foo()
foo.version = "6C115"
foo.version = "6C115"
outputs "version is now 6C115” once and only once.
Post by Quincey Morris
And again, in the converse scenario (automatic KVO, where you want notifications unconditionally) the “dynamic” keyword isn’t optional.
If you’re sending the notifications manually in the accessor, it’s pretty darn guaranteed that those notifications are going to be sent.
Post by Quincey Morris
Post by Jean-Daniel
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.
That example is incorrect, because you left out the @objc on automaticallyNotifiesObserversOfVersion, which will cause it to subtly stop working after SE-0160 is implemented.

Charles

_______________________________________________

Cocoa-dev mailing list (Cocoa-***@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/gegs%40ml-in.narkive.net

This email sent to
Quincey Morris
2017-04-17 21:04:45 UTC
Permalink
If you’re sending them yourself, then what matters is that you don’t *accidentally* let the property generate automatic notifications as well. There is only one way (AFAIK) to *guarantee* that automatic notifications aren’t generated as well, and that’s by returning false from ‘automaticallyNotifiesObserversOfVersion’. There’s no other way in Swift (AFAIK) to ensure that, since there’s no “nondynamic” keyword. There’s also no other way in Obj-C (AFAIK) to ensure that.

This issue wasn’t supposed to be about Swift, though Swift makes it a bit murkier. Rick’s original code was wrong unless he had an Obj-C automaticallyNotifiesObserversOfVersion method elsewhere. For all I know, since he didn’t weigh back in with more information, he had that method all along.
You also forgot the automaticallyNotifiesObserversOfVersion property in the first bit of my example.
Yes, sorry, I just didn’t see it tucked up against the comment. Your code was correct in every detail.

_______________________________________________

Cocoa-dev mailing list (Cocoa-***@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/gegs%40ml-in.narkive.net

This email se
Rick Mann
2017-04-17 21:23:55 UTC
Permalink
Thanks, Charles, that's helpful. In the end I left the class as Obj-C, because I still had to derive from it in Obj-C and you annoyingly can't do that.
Post by Charles Srstka
Post by Rick Mann
-------------
@synthesize version = mVersion
- (void)
setVersion: (NSString *) inVersion
{
if (inVersion == nil && mVersion == nil)
{
return;
}
if ([inVersion isEqualToString: mVersion])
{
return;
}
mVersion = inVersion;
}
-------------
Now I want to translate this method into Swift. Thing is, AFAIK you can't name the ivar created for a property. Is there a way to translate this to swift?
// Only needed if the property will be accessed by Objective-C code, since Swift code won’t see the swizzled accessors anyway for a non-dynamic property.
@objc private static let automaticallyNotifiesObserversOfVersion: Bool = false
// Our actual version property. If you want, you can create a private property named “mVersion” and it will function like an instance variable.
// But I’d probably just use willSet and didSet.
// 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))
}
}
}
Charles
--
Rick Mann
***@latencyzero.com


_______________________________________________

Cocoa-dev mailing list (Cocoa-***@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/gegs%40ml-in.nark
Loading...