使用协议以typealias作为属性

Dán*_*agy 6 generics protocols swift

我有一个带有类型别名的协议:

protocol Archivable {
    typealias DataType

    func save(data: DataType, withNewName newName: String) throws
    func load(fromFileName fileName: String) throws -> DataType
}
Run Code Online (Sandbox Code Playgroud)

以及符合该协议的类:

class Archiver: Archivable {
    typealias DataType = Int

    func save(data: DataType, withNewName newName: String) throws {
        //saving
    }

    func load(fromFileName fileName: String) throws -> DataType {
        //loading
    }
}
Run Code Online (Sandbox Code Playgroud)

我想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
}
Run Code Online (Sandbox Code Playgroud)

但是失败了

协议“可存档”只能用作通用约束,因为它具有“自我”或相关类型要求

我的目标是TestClass只能看到Archiveras Archiveable,因此,如果我想更改保存/加载机制,我只需要创建一个新类,使其符合Archivablein中的属性设置TestClass,但是我不知道这是否可行,如果是这样,那么如何。

而且我想避免使用AnyObject代替DataType。

Hec*_*tos 3

根据您实际想要执行的操作,这可以使用类型擦除来实现。如果您按照评论中发布的链接R Menke中的说明进行操作,您就可以实现您想要做的事情。由于您的属性 inTestClass似乎是一个 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)
    }
}
Run Code Online (Sandbox Code Playgroud)

与 Swift 非常相似AnySequence,您可以像这样将您的类包装Archiver在此类中TestClass

class TestClass {
    let archiver = AnyArchiver(Archiver())
}
Run Code Online (Sandbox Code Playgroud)

通过类型推断,Swift 会将TestClass' archiver let const 键入为AnyArchiver<Int>. 这样做将确保您不必创建十几个协议来定义、、等DataType内容。相反,您可以选择使用泛型来定义变量,如下所示:StringArchiverArrayArchiverIntArchiver

let intArchiver: AnyArchiver<Int>
let stringArchiver: AnyArchiver<String>
let modelArchiver: AnyArchiver<Model>
Run Code Online (Sandbox Code Playgroud)

而不是像这样重复代码:

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
Run Code Online (Sandbox Code Playgroud)

我写了一篇关于此问题的文章,其中包含更详细的内容,以防您在使用此方法时遇到任何问题。我希望这有帮助!