我试着使用两个相互关联的通用协议:
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,但是我找不到问题所在。
最佳答案
发生这种情况是因为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 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>()