NSKeyValueObservingCustomization relies on a global table to map String key paths back into AnyKeyPath 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 the NSKeyValueObservingCustomization 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 the NSKeyValueObservingCustomization method.
Assuming we can rely on the fact that these methods will be invoked synchronously during the observation and never at any other time, we could fix this by using a per-thread table instead, which we populate prior to the observation and then clear afterwards.