[Pitch] Changing NSObject dispatch behavior

The problem with that code isn't that `dynamic` doesn't work for
computed properties. It does; if you mutate the `foo` property,
you'll get the KVO notifications. The problem is you have one
property that depends on another and you didn't set up the KVO
machinery properly using automaticallyNotifiesObservers(forKey:) or
automaticallyNotifiesObserversOf<key>() (incidentally in Swift you
can write the latter as a `static let`, since that becomes a class
method in Obj-C).

You’ll only get the notifications if you mutate ‘foo’ directly. This,
however, is fairly useless, because if you are watching ‘foo’, you
want to be notified every time the value changes, not just when
someone hits one particular accessor. Code relying on observation of
‘foo’ in the example I provided would be prone to breaking in
mysterious and possibly horrible ways.

No, if you implement keyPathsForValuesAffecting<key>() then you get
"foo" KVO notifications when "bar" is mutated. That's the whole
point of that method, and this is exactly what you have to do in Obj-
C as well.

So yes, `dynamic` by itself doesn't mean that the property supports
KVO. But there are very few reasons to use `dynamic` outside of
supporting KVO, so it's a pretty good signal that the property does
support it. And conversely, not having `dynamic` doesn't mean that it
doesn't support KVO, though if it does have manual KVO support using
will/didChangeValue(forKey:) then it should be documented as such.

Use of the ‘dynamic’ keyword enables all manner of runtime hackery
which someone may be employing. The trick to automatically add KVO
conformance to accessors is probably the most common, but it’s hardly
the only one. One also might want to declare things ‘dynamic’ when
working with Objective-C frameworks not under one’s control which
might assume the ability to do metaprogramming on your classes

That is exceedingly rare. I can't even remember the last time I used
such a thing.

I know it’s commonplace to use ‘dynamic’ all over the place wherever
Core Data is involved.

It is? Why? Maybe you're confusing this with Obj-C's @dynamic keyword,
which is completely unrelated to Swift's `dynamic`. When writing Swift
NSManagedObject subclasses, you use the @NSManaged property attribute,
not the `dynamic` keyword (@NSManaged does effectively the same thing
that Obj-C's @dynamic, except it's reserved for integration with
CoreData instead of being as generic as Obj-C's @dynamic is).

Long story short, ‘dynamic’ does not guarantee KVO conformance in any
way, shape, or form.

And declaring that your property returns a String doesn't guarantee that
it actually does either. You can always write broken code. But `dynamic`
is required for automatic KVO conformance, and it's extremely rare to
have a reason to use `dynamic` outside of KVO, so it's a really really
strong signal that the property supports KVO. If you're using `dynamic`
on a property but don't support KVO correctly, as you showed in your
code example, that's a bug with your code.

-Kevin Ballard

···

On Thu, Dec 15, 2016, at 03:01 PM, Charles Srstka wrote:

On Dec 15, 2016, at 4:33 PM, Kevin Ballard <kevin@sb.org> wrote:

On Dec 15, 2016, at 4:35 PM, Kevin Ballard <kevin@sb.org> wrote:

Oops, I mean keyPathsForValuesAffectingValue(forKey:) and
keyPathsForValuesAffecting<Key>().

That said, your code as written actually sends 2 KVO change notices
for "bar", and you do still need to implement
automaticallyNotifiesObservers… to disable the automatic KVO notice
for "bar".

Only when the accessor is called from Objective-C, in which the message-
send is *always* dynamic and the presence of the ‘dynamic’ keyword is
fairly academic. The example, which was simplified for the purpose of
clarity, was to illustrate how things work with respect to Swift’s
vtable dispatch system. A real-world example could shut off the
automatic notifications, or declare the property as @nonobjc, or
similar.

What? No. You marked the property as `dynamic`, which means it'll always
go through message send, which means it'll invoke automatic KVO
conformance and trigger 2 KVO notifications. And you can't mark a
property both `@nonobjc` and `dynamic` as the latter implies the former.

-Kevin Ballard

The problem with that code isn't that `dynamic` doesn't work for computed properties. It does; if you mutate the `foo` property, you'll get the KVO notifications. The problem is you have one property that depends on another and you didn't set up the KVO machinery properly using automaticallyNotifiesObservers(forKey:) or automaticallyNotifiesObserversOf<key>() (incidentally in Swift you can write the latter as a `static let`, since that becomes a class method in Obj-C).

You’ll only get the notifications if you mutate ‘foo’ directly. This, however, is fairly useless, because if you are watching ‘foo’, you want to be notified every time the value changes, not just when someone hits one particular accessor. Code relying on observation of ‘foo’ in the example I provided would be prone to breaking in mysterious and possibly horrible ways.

No, if you implement keyPathsForValuesAffecting<key>() then you get "foo" KVO notifications when "bar" is mutated. That's the whole point of that method, and this is exactly what you have to do in Obj-C as well.

Right… the sentence I was quoting was talking about code which uses ‘dynamic’ but *doesn’t* use keyPathsForValuesAffecting<key>. You’ll get notifications if someone calls that one particular accessor, but otherwise you won’t.

So yes, `dynamic` by itself doesn't mean that the property supports KVO. But there are very few reasons to use `dynamic` outside of supporting KVO, so it's a pretty good signal that the property does support it. And conversely, not having `dynamic` doesn't mean that it doesn't support KVO, though if it does have manual KVO support using will/didChangeValue(forKey:) then it should be documented as such.

Use of the ‘dynamic’ keyword enables all manner of runtime hackery which someone may be employing. The trick to automatically add KVO conformance to accessors is probably the most common, but it’s hardly the only one. One also might want to declare things ‘dynamic’ when working with Objective-C frameworks not under one’s control which might assume the ability to do metaprogramming on your classes

That is exceedingly rare. I can't even remember the last time I used such a thing.

You used such a thing the last time you used KVO. ;-)

I know it’s commonplace to use ‘dynamic’ all over the place wherever Core Data is involved.

It is? Why? Maybe you're confusing this with Obj-C's @dynamic keyword, which is completely unrelated to Swift's `dynamic`. When writing Swift NSManagedObject subclasses, you use the @NSManaged property attribute, not the `dynamic` keyword (@NSManaged does effectively the same thing that Obj-C's @dynamic, except it's reserved for integration with CoreData instead of being as generic as Obj-C's @dynamic is).

@NSManaged implies dynamic, though. Core Data is entirely built on the dynamic runtime, and is using it pretty much everywhere.

Long story short, ‘dynamic’ does not guarantee KVO conformance in any way, shape, or form.

And declaring that your property returns a String doesn't guarantee that it actually does either. You can always write broken code. But `dynamic` is required for automatic KVO conformance, and it's extremely rare to have a reason to use `dynamic` outside of KVO, so it's a really really strong signal that the property supports KVO. If you're using `dynamic` on a property but don't support KVO correctly, as you showed in your code example, that's a bug with your code.

Correlation does not imply causation. Dynamic properties often support KVO, but the developer may have just declared it dynamic in order to use Objective-C’s dynamism to solve some other problem. You don’t know, and thus I certainly wouldn’t call it a bug in the code.

Charles

···

On Dec 16, 2016, at 12:36 AM, Kevin Ballard <kevin@sb.org> wrote:
On Thu, Dec 15, 2016, at 03:01 PM, Charles Srstka wrote:

On Dec 15, 2016, at 4:33 PM, Kevin Ballard <kevin@sb.org <mailto:kevin@sb.org>> wrote:

The problem with that code isn't that `dynamic` doesn't work for
computed properties. It does; if you mutate the `foo` property,
you'll get the KVO notifications. The problem is you have one
property that depends on another and you didn't set up the KVO
machinery properly using automaticallyNotifiesObservers(forKey:) or
automaticallyNotifiesObserversOf<key>() (incidentally in Swift you
can write the latter as a `static let`, since that becomes a class
method in Obj-C).

You’ll only get the notifications if you mutate ‘foo’ directly.
This, however, is fairly useless, because if you are watching ‘foo’,
you want to be notified every time the value changes, not just when
someone hits one particular accessor. Code relying on observation of
‘foo’ in the example I provided would be prone to breaking in
mysterious and possibly horrible ways.

No, if you implement keyPathsForValuesAffecting<key>() then you get
"foo" KVO notifications when "bar" is mutated. That's the whole
point of that method, and this is exactly what you have to do in Obj-
C as well.

Right… the sentence I was quoting was talking about code which uses
‘dynamic’ but *doesn’t* use keyPathsForValuesAffecting<key>. You’ll
get notifications if someone calls that one particular accessor, but
otherwise you won’t.

You can always write buggy code. If you're using `dynamic` for KVO
purposes, then not implementing keyPathsForValuesAffecting<Key> in this
case is strictly a bug. If you're using `dynamic` for something other
than KVO, and your property won't end up supporting KVO properly by
default, then you should document it as such, given that the natural
assumption for a `dynamic` property is that it supports KVO.

So yes, `dynamic` by itself doesn't mean that the property supports
KVO. But there are very few reasons to use `dynamic` outside of
supporting KVO, so it's a pretty good signal that the property does
support it. And conversely, not having `dynamic` doesn't mean that
it doesn't support KVO, though if it does have manual KVO support
using will/didChangeValue(forKey:) then it should be documented as
such.

Use of the ‘dynamic’ keyword enables all manner of runtime hackery
which someone may be employing. The trick to automatically add KVO
conformance to accessors is probably the most common, but it’s
hardly the only one. One also might want to declare things ‘dynamic’
when working with Objective-C frameworks not under one’s control
which might assume the ability to do metaprogramming on your classes

That is exceedingly rare. I can't even remember the last time I used
such a thing.

You used such a thing the last time you used KVO. ;-)

We were talking about metaprogramming other than KVO.

I know it’s commonplace to use ‘dynamic’ all over the place wherever
Core Data is involved.

It is? Why? Maybe you're confusing this with Obj-C's @dynamic
keyword, which is completely unrelated to Swift's `dynamic`. When
writing Swift NSManagedObject subclasses, you use the @NSManaged
property attribute, not the `dynamic` keyword (@NSManaged does
effectively the same thing that Obj-C's @dynamic, except it's
reserved for integration with CoreData instead of being as generic as
Obj-C's @dynamic is).

@NSManaged implies dynamic, though. Core Data is entirely built on the
dynamic runtime, and is using it pretty much everywhere.

@NSManaged could easily be implemented by codegenning static-dispatch
methods that invoke the primitive accessors and will/didChange KVO
broadcasts. Presumably it doesn't (presumably it does literally the same
thing @dynamic does in Obj-C, which is that it makes the assumption that
the methods exist at runtime even though they're unknown at compile-
time), but there's nothing about @NSManaged that requires it to always
use dynamic dispatch. Core Data leverages the dynamic runtime for method
generation, but it doesn't actually require it (you can completely
ignore dynamicism and implement your properties in terms of
primitiveValue(forKey:) and setPrimitiveValue(_:forKey:), though the dynamically-
synthesized primitive accessors are supposed to be more efficient).

Long story short, ‘dynamic’ does not guarantee KVO conformance in
any way, shape, or form.

And declaring that your property returns a String doesn't guarantee
that it actually does either. You can always write broken code. But
`dynamic` is required for automatic KVO conformance, and it's
extremely rare to have a reason to use `dynamic` outside of KVO, so
it's a really really strong signal that the property supports KVO. If
you're using `dynamic` on a property but don't support KVO correctly,
as you showed in your code example, that's a bug with your code.

Correlation does not imply causation. Dynamic properties often support
KVO, but the developer may have just declared it dynamic in order to
use Objective-C’s dynamism to solve some other problem. You don’t
know, and thus I certainly wouldn’t call it a bug in the code.

If a particular language feature is used 99.9% of the time to provide
some particular functionality, then people will assume the presence of
that language feature implies that functionality. I don't think it's
incorrect to assume that the overwhelming majority of uses of `dynamic`
are for KVO conformance.

-Kevin Ballard

···

On Fri, Dec 16, 2016, at 06:30 AM, Charles Srstka wrote:

On Dec 16, 2016, at 12:36 AM, Kevin Ballard <kevin@sb.org> wrote:
On Thu, Dec 15, 2016, at 03:01 PM, Charles Srstka wrote:

On Dec 15, 2016, at 4:33 PM, Kevin Ballard <kevin@sb.org> wrote:

The problem with that code isn't that `dynamic` doesn't work for computed properties. It does; if you mutate the `foo` property, you'll get the KVO notifications. The problem is you have one property that depends on another and you didn't set up the KVO machinery properly using automaticallyNotifiesObservers(forKey:) or automaticallyNotifiesObserversOf<key>() (incidentally in Swift you can write the latter as a `static let`, since that becomes a class method in Obj-C).

You’ll only get the notifications if you mutate ‘foo’ directly. This, however, is fairly useless, because if you are watching ‘foo’, you want to be notified every time the value changes, not just when someone hits one particular accessor. Code relying on observation of ‘foo’ in the example I provided would be prone to breaking in mysterious and possibly horrible ways.

No, if you implement keyPathsForValuesAffecting<key>() then you get "foo" KVO notifications when "bar" is mutated. That's the whole point of that method, and this is exactly what you have to do in Obj-C as well.

Right… the sentence I was quoting was talking about code which uses ‘dynamic’ but *doesn’t* use keyPathsForValuesAffecting<key>. You’ll get notifications if someone calls that one particular accessor, but otherwise you won’t.

You can always write buggy code. If you're using `dynamic` for KVO purposes, then not implementing keyPathsForValuesAffecting<Key> in this case is strictly a bug. If you're using `dynamic` for something other than KVO, and your property won't end up supporting KVO properly by default, then you should document it as such, given that the natural assumption for a `dynamic` property is that it supports KVO.

If you’re using ‘dynamic’ for KVO purposes, you should document it as such. If you are using it for something else, not implementing KVO is not in any way a bug. Relying on KVO for properties that are not documented to be compliant, though, *is.*

Documentation is the only reliable way to check for KVO conformance. I have a class with 28 KVO-compliant methods on it. Of those, exactly 4 are marked as ‘dynamic’. The presence of the ‘dynamic’ keyword is not a good thing to rely on for this (and the lack of a good built-in mechanism is part of why, IMO, we need something new to replace KVO).

So yes, `dynamic` by itself doesn't mean that the property supports KVO. But there are very few reasons to use `dynamic` outside of supporting KVO, so it's a pretty good signal that the property does support it. And conversely, not having `dynamic` doesn't mean that it doesn't support KVO, though if it does have manual KVO support using will/didChangeValue(forKey:) then it should be documented as such.

Use of the ‘dynamic’ keyword enables all manner of runtime hackery which someone may be employing. The trick to automatically add KVO conformance to accessors is probably the most common, but it’s hardly the only one. One also might want to declare things ‘dynamic’ when working with Objective-C frameworks not under one’s control which might assume the ability to do metaprogramming on your classes

That is exceedingly rare. I can't even remember the last time I used such a thing.

You used such a thing the last time you used KVO. ;-)

We were talking about metaprogramming other than KVO.

We were talking about libraries and/or frameworks that have the ability to do metaprogramming, which when you come down to it is all KVO is. KVO is the most popular of these, but keep in mind that all Objective-C code written before 2014 is assuming that *every* method is *always* going to be dynamically dispatched, and as a result may act on those assumptions. For example, consider the following contrived example:

import Foundation

class Foo: NSObject {
    @objc func sayHello() {
        print("Hello World")
    }
}

class Bar: NSObject {
    @objc func doSomething(with foo: Foo)
        foo.sayHello()
    }
}

let foo = Foo()
let bar = Bar()

SomeLegacyObjectiveCAPI().doSomething(with: foo, andSendItBackTo: bar, selector: selector(Bar.doSomething(with:)))

Is this safe? On the surface, it looks like it. However, for all we know, the Objective-C API may send us back an NSProxy pretending to be our Foo object, rather than the Foo object itself. This is perfectly legal by Objective-C conventions, the proxy will look like a Foo due to isKindOfClass: being forwarded, and if we try to statically access its “sayHello()” method on it, we’ll crash. Therefore, it would be a best practice in this situation to declare sayHello() as dynamic to avoid this potential issue. This use of ‘dynamic’ would not have anything to do with KVO, however, and if someone assumed it did, that would be their bug.

And you never know, someone might have just used ‘dynamic’ for some reason totally unrelated to metaprogramming; say, someone writes a callback method that will only ever be accessed by the Objective-C runtime, and declares it ‘dynamic’ to formalize that. Or maybe someone just uses ‘dynamic’ in place of ‘@objc’ because they don’t like the ugly @ symbol. Or maybe the code was written by someone new to Swift who got burned in the past by some method not being seen by the Objective-C runtime for target-action or KVO or whatever and now just reflexively puts ‘dynamic’ on everything because that’s what fixed it that one time and they don’t really understand why. When you’re dealing with other people’s code, you just don’t really know, unless you know the author or can read their mind.

Long story short, ‘dynamic’ does not guarantee KVO conformance in any way, shape, or form.

And declaring that your property returns a String doesn't guarantee that it actually does either. You can always write broken code. But `dynamic` is required for automatic KVO conformance, and it's extremely rare to have a reason to use `dynamic` outside of KVO, so it's a really really strong signal that the property supports KVO. If you're using `dynamic` on a property but don't support KVO correctly, as you showed in your code example, that's a bug with your code.

Correlation does not imply causation. Dynamic properties often support KVO, but the developer may have just declared it dynamic in order to use Objective-C’s dynamism to solve some other problem. You don’t know, and thus I certainly wouldn’t call it a bug in the code.

If a particular language feature is used 99.9% of the time to provide some particular functionality, then people will assume the presence of that language feature implies that functionality. I don't think it's incorrect to assume that the overwhelming majority of uses of `dynamic` are for KVO conformance.

In Objective-C, a method that returns an NSArray, the great majority of the time, is returning an immutable array. However, the contract does not promise this; the array could easily, and legally, actually be an NSMutableArray internally. So if you’re writing Objective-C code and using a method that returns an array, even though that array *probably* won’t be mutated after you get it, it’s still a best practice to store the array in a @property declared (copy) rather than (strong), to ensure that it won’t be mutated behind our backs. It doesn’t matter if that would only happen 0.1% of the time (a figure I dispute in the case of ‘dynamic', but this is beside the point), because we’re all familiar with Murphy’s Law: if it can go wrong, it will. Making assumptions based on “well, this probably won’t happen” is just heuristics, which isn’t a good idea when things need to be rock solid.

Charles

···

On Dec 19, 2016, at 1:57 PM, Kevin Ballard <kevin@sb.org> wrote:
On Fri, Dec 16, 2016, at 06:30 AM, Charles Srstka wrote:

On Dec 16, 2016, at 12:36 AM, Kevin Ballard <kevin@sb.org <mailto:kevin@sb.org>> wrote:
On Thu, Dec 15, 2016, at 03:01 PM, Charles Srstka wrote:

On Dec 15, 2016, at 4:33 PM, Kevin Ballard <kevin@sb.org <mailto:kevin@sb.org>> wrote: