在Swift 4中,当keypath和value的类型是通用的但是相同时,如何分配给keypath?

Com*_*ast 4 generics swift4

鉴于:

struct Foo {
    var bar: String = “”
    var baz: Int? = nil
}
let values: [Any] = [“foo”, 1337]
let fooPaths: [PartialKeyPath<Foo>] = [\Foo.bar, \Foo.baz] 
let protoFoo = Foo()
Run Code Online (Sandbox Code Playgroud)

如何创建迭代这些键路径和值的for循环,并在类型匹配两者且keypath是可写的时将值分配给键路径?

注意:它必须使用泛型来完成,因为Foo可以具有任何类型的属性(当然,除了类型Foo).

我尝试过这样的东西:

func setKeyOnRoot<Root, Value>(_ root: Root, value: Value, key: PartialKeyPath<Root>) -> Root {
    var root = root
    if let writableKey = key as? WritableKeyPath<Root, Value> {
        root[keyPath: writableKey] = value
    }
    return root
}
Run Code Online (Sandbox Code Playgroud)

但由于在调用此函数之前必须将值转换为Any,因此运行时无法看到类型Value的含义.

Dan*_*all 6

此实现类似于您作为您尝试的方法示例提供的内容,我相信它会产生您正在寻找的结果:

struct WritableKeyPathApplicator<Type> {
    private let applicator: (Type, Any) -> Type
    init<ValueType>(_ keyPath: WritableKeyPath<Type, ValueType>) {
        applicator = {
            var instance = $0
            if let value = $1 as? ValueType {
                instance[keyPath: keyPath] = value
            }
            return instance
        }
    }
    func apply(value: Any, to: Type) -> Type {
        return applicator(to, value)
    }
}

struct Foo {
    var bar: String = ""
    var baz: Int? = nil
}

let values: [Any] = ["foo", 1337]
let fooPaths: [WritableKeyPathApplicator<Foo>] = [WritableKeyPathApplicator(\Foo.bar), WritableKeyPathApplicator(\Foo.baz)]
let protoFoo = zip(fooPaths, values).reduce(Foo()){ return $1.0.apply(value: $1.1, to: $0) }

print(protoFoo)  // prints Foo(bar: "foo", baz: Optional(1337))
Run Code Online (Sandbox Code Playgroud)

这个版本做同样的事情,但使用原始问题中指定的for循环可以用以下代码替换倒数第二行:

var protoFoo = Foo()
for (applicator, value) in zip(fooPaths, values) {
    protoFoo = applicator.apply(value: value, to: protoFoo)
}
Run Code Online (Sandbox Code Playgroud)

请注意,需要某些类型擦除才能使用在同一根类型上运行但具有不同值类型的单个keyPath数组.但是,在运行时,内部的闭包WritableKeyPathApplicator可以专门为每个keyPath强制转换为正确的原始值类型,并且如果它是错误的类型(如上面的示例代码中那样),则默默地跳过该值,否则可能会抛出错误或打印更多有用的信息到控制台等.

  • @CommaToast - 虽然我以前没有将它用于 keyPaths,但这是 Swift 类型橡皮擦的常见模式,我在各种情况下都使用过它。几年前,当我第一次看到这个例子时,我大吃一惊:) https://gist.github.com/JadenGeller/f0d05a4699ddd477a2c1 (2认同)