首页 > 解决方案 > 创建一个可复制的协议默认初始化器

问题描述

我得到了这个功能:

public protocol ContentType {
    associatedtype T

    static var allTypes: [T] { get }
    static func getContentType(contentTypeId: String) -> T

    var contentTypeId: String? { get }
}

public protocol Copyable: class {
    associatedtype T

    static func copy(old: T, new: T)
}

public protocol CopyableContentType: class {
    associatedtype T
    init(existingContentType: T, newContentTypeId: String?)
}

我想为一个类提供一个默认初始化程序:

使用上面的代码,我总是在所需的 init 中做同样的事情:调用实现类的复制函数。有没有办法在保留这个初始化程序的同时省略跨类的重复代码?我想把它添加到协议扩展中:

public extension CopyableContentType where Self: ContentType & Copyable {
    typealias T = Self // So wrong but I have no idea what to put here
                       // T should be the same T as the T usud in ContentType and Copyable
    init(existingContentType: T, newContentTypeId: String?) {
        Self.copy(old: existingContentType, new: self)
    }
}

但这会导致一些错误。我不知道在 typealias 中输入什么,我不能使用相关类型。有什么方法可以提供调用复制函数的默认初始化程序?

标签: swiftprotocolstype-aliasassociated-types

解决方案


我想为一个类提供一个默认初始化程序:

  • 符合ContentType
  • 符合Copyable

我将尝试回答这个问题,假设您的意思是:

  • 如果符合的类型也符合. init(existingContentType: T, newContentTypeId: String?)_ ContentType_ContentTypeCopyable

让我们先看看你的Copyable协议。这可能是由于您的问题中省略了细节/用例,但我在这里看不到相关类型的需要T,因为协议基本上蓝图/承诺class符合的类型Copyable,例如TheClassType,将提供static copy(from: TheClassType, to: TheClassType)函数” ——其中符合类型 ( TheClassType) 只是Self. IE:

protocol Copyable: class {
    static func copy(from: Self, to: Self)
}

这同样适用于ContentType:这里是否需要一个associatedType,或者ContentType给定的具体类型只是类型本身;即Self?在继续之前,让我们去掉与您的问题无关的部分:

protocol ContentType {
    var contentTypeId: String? { get }
}

现在,在任何初始化程序复制(而不是分配 - 我们正在处理引用类型)任何东西 self(例如 的值类型成员self)之前,self必须已经初始化(或分配给)。因此,为了允许为(在符合;的情况下是一种类型)的init(existingContentType: Self, newContentTypeId: String?)初始化器提供默认实现——其中实现旨在使用的蓝图——符合的类型必须知道一种在之前初始化自身的方法复制步骤。即,需要在调用方法之前对一些可用于在“复制初始化程序”中初始化的初始化程序进行蓝图。让我们简单地绘制蓝图ContentTypeCopyableSelfclasscopy(from: Self, to: Self)copyableContentTypeContentTypeselfcopy(from:to)init()

protocol ContentType {
    var contentTypeId: String? { get set }
    init()
}

现在,由于ContentType蓝图是一个contentTypeId成员,并且复制初始化程序包含一个newContentTypeId参数,因此提供一个默认实现的初始化程序contentTypeId作为其唯一参数可能是明智的;即init(contentTypeId: String?)

extension ContentType {
    init(contentTypeId: String?) {
        self.init()
        self.contentTypeId = contentTypeId
    }
}

有了这个,我们可以提供默认实现的init(existingContentType: Self, newContentTypeId: String?)初始化程序作为ContentType基于 to 一致性的Self约束扩展Copyable

extension ContentType where Self: Copyable {
    init(existingContentType: Self, newContentTypeId: String?) {
        self.init(contentTypeId: newContentTypeId)
        Self.copy(from: existingContentType, to: self)
    }
}

例子

综上所述:

protocol Copyable: class {
    static func copy(from: Self, to: Self)
}

protocol ContentType {
    var contentTypeId: String? { get set }
    init()
}

extension ContentType {
    init(contentTypeId: String?) {
        self.init()
        self.contentTypeId = contentTypeId
    }
}

extension ContentType where Self: Copyable {
    init(existingContentType: Self, newContentTypeId: String?) {
        self.init(contentTypeId: newContentTypeId)
        Self.copy(from: existingContentType, to: self)
    }
}

例子:

// Copyable content type
final class Foo: ContentType, Copyable {
    // Note that since all stored properties have initial values,
    // the compiler provides a synthesized initializer for init().
    var contentTypeId: String?
    var data = 0

    static func copy(from: Foo, to: Foo) {
        to.data = from.data
    }
}

let foo1 = Foo(contentTypeId: "foo1")
foo1.data = 42
let foo2 = Foo(existingContentType: foo1, newContentTypeId: "foo2")

print(foo1.contentTypeId ?? "None", foo1.data) // foo1 42
print(foo2.contentTypeId ?? "None", foo2.data) // foo2 42


// Non-copyable content type
final class Bar: ContentType {
    var contentTypeId: String?
} // Bar has no access to
  // init(existingContentType: Self, newContentTypeId: String?)

推荐阅读