我试着使用两个相互关联的通用协议:

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'whileStringDataStore被定义为符合DataStore
我读过一些关于通用协议的好资料,包括SO和这个Medium post,但是我找不到问题所在。

最佳答案

发生这种情况是因为typealiasassociatedtype应该有具体化,而不是抽象化。
因此,对于您的情况,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 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>()

10-01 22:23