我有一个带有typealias的协议: protocol Archivable { typealias DataType func save(data: DataType, withNewName newName: String) throws func load(fromFileName fileName: String) throws - DataType} 以及符合该协议的类: class
protocol Archivable { typealias DataType func save(data: DataType, withNewName newName: String) throws func load(fromFileName fileName: String) throws -> DataType }
以及符合该协议的类:
class Archiver: Archivable { typealias DataType = Int func save(data: DataType, withNewName newName: String) throws { //saving } func load(fromFileName fileName: String) throws -> DataType { //loading } }
我想在另一个类中使用Archivable作为属性,如:
class TestClass { let arciver: Archivable = Archiver() //error here: Protocol 'Archivable' can only be used as a generic constraint because it has Self or associated type requiments }
但它失败了
Protocol ‘Archivable’ can only be used as a generic constraint because it has Self or associated type requiments
我的目标是TestClass只能看到Archiver为Archiveable,所以如果我想更改保存/加载机制,我只需要创建一个符合Archivable的新类,因为它设置为TestClass中的属性,但我不是知道这是否有道理,如果是,那么如何.
我想避免使用AnyObject而不是DataType.
根据您实际尝试的操作,这可以使用类型擦除.如果您按照评论中发布的链接 R Menke中的说明操作,则可以实现您的目标.由于TestClass中的属性似乎是一个let,我假设你已经知道编译时DataType的类型.首先,您需要设置类型已删除的Archivable类,如下所示:class AnyArchiver<T>: Archivable { private let _save: ((T, String) throws -> Void) private let _load: (String throws -> T) init<U: Archivable where U.DataType == T>(_ archiver: U) { _save = archiver.save _load = archiver.load } func save(data: T, withNewName newName: String) throws { try _save(data, newName) } func load(fromFileName fileName: String) throws -> T { return try _load(fileName) } }
就像Swift的AnySequence一样,你可以将你的Archiver包装在TestClass中的这个类中,如下所示:
class TestClass { let archiver = AnyArchiver(Archiver()) }
通过类型推断,Swift将输入TestClass的archiver let constant作为AnyArchiver< Int>.这样做将确保您不必创建十几个协议来定义DataType,如StringArchiver,ArrayArchiver,IntArchiver等.您可以选择使用泛型来定义变量,如下所示:
let intArchiver: AnyArchiver<Int> let stringArchiver: AnyArchiver<String> let modelArchiver: AnyArchiver<Model>
而不是复制像这样的代码:
protocol IntArchivable: Archivable { func save(data: Int, withNewName newName: String) throws func load(fromFileName fileName: String) throws -> Int } protocol StringArchivable: Archivable { func save(data: String, withNewName newName: String) throws func load(fromFileName fileName: String) throws -> String } protocol ModelArchivable: Archivable { func save(data: Model, withNewName newName: String) throws func load(fromFileName fileName: String) throws -> Model } let intArchiver: IntArchivable let stringArchiver: StringArchivable let modelArchiver: ModelArchivable
我写了一篇关于goes into even more detail的帖子,以防你遇到这种方法的任何问题.我希望这有帮助!