Swift 泛型和协议

时间:2021-03-01 19:40:01

标签: swift generics protocols

鉴于以下情况,这将引发关于协议无法遵守自身并且只有结构/枚举可以遵守协议的编译时错误。这似乎违背了能够在泛型中使用协议的目的。我试图理解为什么这不起作用,但是如果我删除泛型并将协议放在“Z”所在的位置,一切都很好。这似乎与应该允许哪些协议和泛型背道而驰。

**为问题的清晰度进行编辑:我需要采用一种可以转换为 [String:MyProtocol] 字典的 Any 类型,并将其传递给方法 printEmprintEm 必须使用泛型,因为它将实例化类 Z 的实例。

protocol MyProtocol {
  init()
  var whoAmI:String { get }
}

func genericPassing(unknownThing:Any) {
  let knownThing = unknownThing as? [String:MyProtocol]
  if(knownThing != nil){
    self.printEm(knownThing)
  }
}

func printEm<Z:MyProtocol>(theThings:[String:Z]) {
  let zCollection:[Z] = []
  for thing in theThings {
    print(thing.whoAmI)
    zCollection.append(Z())
  }
}

**编辑了 printEm 以说明为什么需要泛型。

** 编辑更复杂的代码。两个主要要求是使用泛型来调用 Z() 以及能够接受 Any 并以某种方式对其进行类型检查和/或强制转换,以便它可以在泛型方法中使用。

private func mergeUpdates<Z:RemoteDataSyncable>(source:inout Z, updates:[WritableKeyPath<Z, Any>:Any]) throws {
        for key in updates.keys {
            let value = updates[key]!
            let valueDict = value as? [String:[WritableKeyPath<RemoteDataSyncable, Any>:Any]]
            if(valueDict != nil) {
                var currentValueArray = source[keyPath: key] as? [RemoteDataSyncable]
                if(currentValueArray != nil) {
                    self.mergeUpdates(source: &currentValueArray!, updates: valueDict!)
                }
                else {
                    throw SyncError.TypeError
                }
            }
            else {
                source[keyPath: key] = value
            }
        }
    }
    
    private func mergeUpdates<Z:RemoteDataSyncable>(source:inout [Z], updates:[String:[WritableKeyPath<Z,Any>:Any]]) {
        for key in updates.keys {
            var currentObject = source.first { syncable -> Bool in
                return syncable.identifier == key
            }
            if(currentObject != nil) {
                try! self.mergeUpdates(source: &currentObject!, updates: updates[key]!)
            }
            else {
                var newSyncable = Z()
                try! self.mergeUpdates(source: &newSyncable, updates: updates[key]!)
                source.append(newSyncable)
            }
        }
    }

1 个答案:

答案 0 :(得分:1)

这是为什么协议不符合自身的完美例子。在您的代码中,ZMyProtocol,所以 Z()MyProtocol()。这将如何运作?它是什么类型?它有多大?然后您不能将它们放入 [Z] 中,因为它们可能是不同的类型。

您的意思是传递任意 MyProtocols 并在每个元素的类型上调用 init

func printEm(theThings:[String: MyProtocol]) {
    var zCollection:[MyProtocol] = []
    for thing in theThings.values {
        print(thing.whoAmI)
        zCollection.append(type(of: thing).init())
    }
}

当我建议使用闭包时,这就是我的意思。这个Updater可以接受任意的ReferenceWritableKeyPaths,当你传递给它任何更新值时,它会分配给每一个可以接受它的键路径。这有点没用,但显示了技术。 (请记住,更新程序会保留对象,因此这可能是您需要解决的问题。)

class Updater {
    private(set) var updaters: [(Any) -> ()] = []

    func add<Root, Value>(keyPath: ReferenceWritableKeyPath<Root, Value>, on root: Root) {
        updaters.append { value in
            if let value = value as? Value {
                root[keyPath: keyPath] = value
            }
        }
    }

    func updateAll(with value: Any) {
        for updater in updaters {
            updater(value)
        }
    }
}

class Client {
    var updateMe: Int = 0
}

let client = Client()

let updater = Updater()
updater.add(keyPath: \.updateMe, on: client)

updater.updateAll(with: 3)

client.updateMe // 3

这段代码中的关键教训是 add 上的泛型类型在编译时被 (Any) -> () 闭包擦除(隐藏)。并且运行时 as? 检查在该闭包内完成,其中的类型都是已知的。