Curious why class setters have computed keypaths

@dan-zheng and I have a test case where we compare class keypaths that we create at runtime and an explicit array of hardcoded keypaths to class members.

class SomeClass {
  var foo = 0
}

let classKeyPaths: [PartialKeyPath<SomeClass>] = createKeyPaths(for: SomeClass.self)
XCTAssertEqual(classKeyPaths, [\SomeClass.foo])

and we're hitting the assertion because the two are not equal. We create keypaths with components that are either struct or class depending on the type, but I found out that the Swift compiler emits computed keypaths for non-final class setters to be dynamically dispatched. Why is that? I tested that the keypaths we create using class kind still appear to work reading/writing to the property of the class.

let someClass = SomeClass()
let fooKeyPath = someClassKeyPaths[0]
someClass[keyPath: fooKeyPath] = 10
print(someClass.foo) // 10
print(someClass[keyPath: fooKeyPath]) // 10

So I dug a little deeper and found that keypath equality also compared the component values which makes sense why we're hitting the assertion because our keypaths have a buffer size of 4, while compiler emitted ones have 32.

  1. Why this decision? Is there some Objective-C consideration here? Is our method supposed to work?
  2. Is there anyway to reconstruct a keypath with the identifier, getter, and setter at runtime? (I think the answer to this one is no.)

cc: @Joe_Groff

1 Like

Unless the property is final, it may be overridden in a subclass, so the key path is computed in that case.

:man_facepalming: Thanks!