NSKeyValueObservingCustomization Is Fundamentally Broken
The breakage is the fact that implementing it relies on being able to do equality testing on key paths, but equality testing on key paths doesn’t work in the face of subclassing. By that I mean given the following code
class Foo: NSObject { @objc dynamic var name: String? } class Bar: Foo {}The expression
\Foo.name == \Bar.name
returnsfalse
even thoughBar
is just inheriting its property fromFoo
. This means that an implementation ofNSKeyValueObservingCustomization
cannot possibly work properly on anything besides a non-final
class. Even if keypath construction in this instance were changed such that\Bar.name
returns the same thing as\Foo.name
, the same cannot be done for the more complicated case[…]
I guess the workaround is to use the string-based Objective-C methods instead of the AnyKeyPath
Swift ones.
See also: SR-9077.
NSKeyValueObservingCustomization
relies on a global table to mapString
key paths back intoAnyKeyPath
values. However, as the string keypath does not include the root type, this means that observing properties with the same name on separate objects will overwrite each other in the global table. In a single-threaded scenario this is acceptable as theNSKeyValueObservingCustomization
methods are invoked synchronously when the observation is created, and the global table is populated immediately prior to creating the observation. However, in a multithreaded scenario, the global keypath table could be overwritten with a different keypath prior to invoking theNSKeyValueObservingCustomization
method.