通过使用Where子句创建扩展名,无法符合协议

JIE*_*ANG 5 swift swift4.1

protocol Typographable {
    func setTypography(_ typography: Typography)
}

extension UILabel: Typographable {}

extension Typographable where Self == UILabel {

    func setTypography(_ typography: Typography) {

        self.font = typography.font
        self.textColor = typography.textColor
        self.textAlignment = typography.textAlignment
        self.numberOfLines = typography.numberOfLines
    }
}
Run Code Online (Sandbox Code Playgroud)

我已经创建了一个协议Typographable,UILabel实现了这个协议,并且实现在extension Typographable where Self == UILabel.

它在swift 4.0中完美运行,但它在swift 4.1中不再起作用,错误信息是 Type 'UILabel' does not conform to protocol 'Typographable'

我仔细阅读了swift 4.1 的CHANGELOG,但是找不到任何有用的东西.

这是正常的,我错过了什么吗?

Ham*_*ish 4

这很有趣。长话短说(好吧,也许没那么短) \xe2\x80\x93 这是#12174有意副作用,它允许协议扩展方法返回以满足非最终类的协议要求,这意味着您现在可以在 4.1 中这样说:Self

\n\n
\n
protocol P {\n  init()\n  static func f() -> Self\n}\n\nextension P {\n  static func f() -> Self {\n    return self.init()\n  }\n}\n\nclass C : P {\n  required init() {}\n}\n
Run Code Online (Sandbox Code Playgroud)\n
\n\n

在 Swift 4.0.3 中,您会在扩展实现上遇到一个令人困惑的错误f()

\n\n
\n

f()非最终类\'C\'中的方法\' \'必须返回Self以符合协议\' P\'

\n
\n\n

这如何应用于您的示例?好吧,考虑一下这个有点相似的例子:

\n\n
class C {}\nclass D : C {}\n\nprotocol P {\n  func copy() -> Self\n}\n\nextension P where Self == C {\n  func copy() -> C {\n    return C()\n  }\n}\n\nextension C : P {}\n\nlet d: P = D()\nprint(d.copy()) // C (assuming we could actually compile it)\n
Run Code Online (Sandbox Code Playgroud)\n\n

如果 Swift 允许协议扩展的实现来满足要求,那么即使在实例上调用copy()我们也会构造实例,从而破坏协议契约。因此,Swift 4.1 使一致性变得非法(为了使第一个示例中的一致性合法),并且无论是否有回报,它都会这样做。CDSelf

\n\n

我们实际上想要用扩展来表达的是Self必须是或继承自 C,这迫使我们考虑子类使用一致性时的情况。

\n\n

在你的例子中,它看起来像这样:

\n\n
protocol Typographable {\n    func setTypography(_ typography: Typography)\n}\n\nextension UILabel: Typographable {}\n\nextension Typographable where Self : UILabel {\n\n    func setTypography(_ typography: Typography) {\n\n        self.font = typography.font\n        self.textColor = typography.textColor\n        self.textAlignment = typography.textAlignment\n        self.numberOfLines = typography.numberOfLines\n    }\n}
Run Code Online (Sandbox Code Playgroud)\n\n

正如Martin 所说,它在 Swift 4.1 中编译得很好。尽管正如马丁也所说,这可以用更直接的方式重写:

\n\n
protocol Typographable {\n    func setTypography(_ typography: Typography)\n}\n\nextension UILabel : Typographable {\n\n    func setTypography(_ typography: Typography) {\n\n        self.font = typography.font\n        self.textColor = typography.textColor\n        self.textAlignment = typography.textAlignment\n        self.numberOfLines = typography.numberOfLines\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n
\n\n

在技​​术细节上,#12174的作用是允许通过见证(一致实现)thunk 传播隐式Self参数。它通过向受限于一致类的 thunk 添加通用占位符来实现此目的。

\n\n

因此,对于这样的一致性:

\n\n
class C {}\n\nprotocol P {\n  func foo()\n}\n\nextension P {\n  func foo() {}\n}\n\nextension C : P {}\n
Run Code Online (Sandbox Code Playgroud)\n\n

在 Swift 4.0.3 中,C\ 的协议见证表(我在这里对 PWT 进行了一些漫谈,这可能有助于理解它们)包含一个具有签名的 thunk 条目:

\n\n
(C) -> Void\n
Run Code Online (Sandbox Code Playgroud)\n\n

(请注意,在我链接到的漫游中,我跳过了 thunk 的细节,只是说 PWT 包含用于满足要求的实现的条目。但语义在很大程度上是相同的)

\n\n

然而在 Swift 4.1 中,thunk 的签名现在看起来像这样:

\n\n
<Self : C>(Self) -> Void\n
Run Code Online (Sandbox Code Playgroud)\n\n

为什么?因为这允许我们传播 的类型信息Self,从而允许我们保留要在第一个示例中构造的实例的动态类型(从而使其合法)。

\n\n

现在,对于如下所示的扩展:

\n\n
extension P where Self == C {\n  func foo() {}\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

(C) -> Void扩展实现的签名和 thunk 的签名不匹配<Self : C>(Self) -> Void。因此编译器拒绝一致性(可以说这太严格了,因为它Self是 的子类型C,我们可以在这里应用逆变,但这是当前的行为)。

\n\n

但是,如果我们有扩展名:

\n\n
extension P where Self : C {\n  func foo() {}\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

一切都恢复正常了,因为两个签名现在都是<Self : C>(Self) -> Void

\n\n

不过,关于#12174需要注意的一件有趣的事情是,当需求包含关联类型时,它会保留旧的 thunk 签名。所以这有效:

\n\n
class C {}\n\nprotocol P {\n  associatedtype T\n  func foo() -> T\n}\n\nextension P where Self == C {\n  func foo() {} // T is inferred to be Void for C.\n}\n\nextension C : P {}\n
Run Code Online (Sandbox Code Playgroud)\n\n

但您可能不应该诉诸如此可怕的解决方法。只需将协议扩展约束更改为where Self : C.

\n


归档时间:

查看次数:

580 次

最近记录:

7 年,5 月 前