如何处理 Swift 中的多个泛型协议?

How to handle multiple generic protocols in Swift?

我正在尝试使用两个相互关联的通用协议:

protocol PersistableData {}

protocol DataStore: class {
    associatedtype DataType: PersistableData

    func save(data: DataType, with key: String)

    func retreive(from key: String) -> DataType?
}

protocol PersistentDataModel {
    // Swift infers that DataType: PersistableData as DataType == DataStoreType.DataType: PersistableData
    // Setting it explicitly makes the compiler fail
    associatedtype DataType
    associatedtype DataStoreType: DataStore where DataStoreType.DataType == DataType
}

extension String: PersistableData {}

protocol StringDataStore: DataStore {
    associatedtype DataType = String
}


class Test: PersistentDataModel {
    typealias DataType = String
    typealias DataStoreType = StringDataStore
}

但是 Xcode 无法编译说 Type 'Test' does not conform to protocol 'PersistentDataModel' 并建议 Possibly intended match 'DataStoreType' (aka 'StringDataStore') does not conform to 'DataStore'StringDataStore 被定义为符合 DataStore

我已经阅读了一些关于通用协议的好资源,包括 SO and this Medium post,但我找不到问题出在哪里。

发生这种情况是因为您的 typealias 对于 associatedtype 应该是具体的,而不是抽象的。

因此,对于您的情况,StringDataStore 应该是 class,而不是 protocol

protocol PersistableData {}

protocol DataStore: class {
associatedtype DataType: PersistableData

    func save(data: DataType, with key: String)

    func retreive(from key: String) -> DataType?
}

protocol PersistentDataModel {
    // Swift infers that DataType: PersistableData as DataType == DataStoreType.DataType: PersistableData
    // Setting it explicitly makes the compiler fail
    associatedtype DataType
    associatedtype DataStoreType: DataStore where DataStoreType.DataType == DataType
}
extension String: PersistableData {}

class StringDataStore: DataStore {
    typealias DataType = String

    func save(data: String, with key: String) {
        //
    }

    func retreive(from key: String) -> String? {
        return nil
    }
}

class Test: PersistentDataModel {
    typealias DataType = String
    typealias DataStoreType = StringDataStore
}

但是,您可以继续使用协议并通过在 Test class:

中使用额外的泛型条件来解决它
class Test<T: StringDataStore>: PersistentDataModel where T.DataType == String {
    typealias DataStoreType = T
    typealias DataType = T.DataType
}

使用这个你可以告诉编译器具体类型将被传递给 Test 其他地方。

像这样:

class ConcreteStringDataStore: StringDataStore {
    func save(data: String, with key: String) {
        //
    }

    func retreive(from key: String) -> String? {
        return nil
    }
}

let test = Test<ConcreteStringDataStore>()