在 Dictionary/HashMap 中存储 KeyPath

Storing KeyPaths in Dictionary/HashMap

我在散列 ReferenceWritableKeyPath 时遇到问题。散列函数似乎在对密钥路径进行散列时也考虑了 ReferenceWritableKeyPath 的通用属性。我已经包含了示例代码来说明为什么这是一个问题:

struct TestStruct<T> {
    // This function should only be callable if the value type of the path reference == T
    func doSomething<Root>(object: Root, path: ReferenceWritableKeyPath<Root, T>) -> Int {
        // Do something
        print("Non-optional path:   \(path)                    \(path.hashValue)")
        return path.hashValue
    }
}

let label = UILabel()
let textColorPath = \UILabel.textColor

let testStruct = TestStruct<UIColor>()
let hash1 = testStruct.doSomething(object: label, path: \.textColor)
let hash2 = textColorPath.hashValue
print("Optional path:       \(textColorPath)    \(hash2)")

如果您 运行 上面的代码,您会注意到 hash1 和 hash2 是不同的,尽管它们是指向相同 属性 UILabel 的路径。

这是因为第一个 ReferenceWritableKeyPath 有一个 ValueUIColor 而第二个 ReferenceWritableKeyPath 有一个 ValueOptional<UIColor>

我的项目要求将 ReferenceWritableKeyPath 存储在字典中,以便关联对象 (UILabel) 的每个 属性 只有一个 keyPath。由于哈希值不同,这意味着相同的路径将在字典中存储为 2 个不同的键。

有谁知道我可以使用它的方法吗?

~提前致谢

使textColorPath也成为非可选的,以匹配:

let textColorPath = \UILabel.textColor!

或明确类型:

let textColorPath: ReferenceWritableKeyPath<UILabel, UIColor> = \.textColor

潜在的问题是 \.textColor 是一个隐式展开的可选而不是“真正的”可选。在某些情况下,它被视为基础类型,而在其他情况下,它被提升为可选类型。它是一个隐式解包可选的原因是因为将 textColor 设置为 nil 是合法的。但是你读到的值永远不会是nil。

正如@Rob Napier 指出的那样,问题出在泛型类型本身。我解决问题的方法是将 doSomething 分成两个单独的方法:

func doSomething<Root>(object: Root, path: ReferenceWritableKeyPath<Root, T?>) -> Int {
    // Do something
    print("Non-optional path:   \(path)                    \(path.hashValue)")
    return path.hashValue
}

func doSomething<Root>(object: Root, path: ReferenceWritableKeyPath<Root, T>) -> Int {
    // Do something
    print("Non-optional path:   \(path)                    \(path.hashValue)")
    return path.hashValue
}

T 是一个可选类型时,第一个将被调用,例如上面的示例(其中 UIColor 可以是 nil)。当 keyPath 指向 non-optional 属性 时调用第二个。 Swift 非常聪明,所以我猜它能够找出调用哪个方法,尽管它们几乎重复 headers.