Swift 无法通过使用Where子句创建扩展来遵守协议

Swift 无法通过使用Where子句创建扩展来遵守协议,swift,swift4.1,Swift,Swift4.1,我已经创建了一个协议typograble,UILabel实现了这个协议,并且实现在扩展typograble中,其中Self==UILabel 它在swift 4.0中运行良好,但在swift 4.1中不再运行,错误消息是类型“UILabel”不符合协议“可打印” 我已经仔细阅读了swift 4.1,但没有发现任何有用的内容 这正常吗,我错过了什么吗?这很有趣。长话短说(好吧,也许没那么短)–它不是of,它允许协议扩展方法返回Self,以满足非最终类的协议要求,这意味着您现在可以在4.1中这样说:

我已经创建了一个协议
typograble
UILabel
实现了这个协议,并且实现在
扩展typograble中,其中Self==UILabel

它在swift 4.0中运行良好,但在swift 4.1中不再运行,错误消息是
类型“UILabel”不符合协议“可打印”

我已经仔细阅读了swift 4.1,但没有发现任何有用的内容


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

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

在Swift 4.0.3中,
f()
的扩展实现会出现一个令人困惑的错误:

非最终类“C”中的方法“
f()
”必须返回
Self
,以符合协议“
p

这如何适用于您的示例?嗯,考虑一下这个类似的例子:

protocol P {
  init()
  static func f() -> Self
}

extension P {
  static func f() -> Self {
    return self.init()
  }
}

class C : P {
  required init() {}
}
如果Swift允许协议扩展实现
copy()
以满足需求,那么即使在调用
d
实例时,我们也会构造
C
实例,从而破坏协议契约。因此,Swift 4.1将一致性定为非法(以使第一个示例中的一致性合法),并且无论是否存在
Self
返回,Swift都会这样做

我们想用扩展来表达的是,<代码>自身<代码>必须继承或继承<代码> c>代码>,这迫使我们考虑子类使用一致性时的情况。

在您的示例中,看起来是这样的:

class C {}
class D : C {}

protocol P {
  func copy() -> Self
}

extension P where Self == C {
  func copy() -> C {
    return C()
  }
}

extension C : P {}

let d: P = D()
print(d.copy()) // C (assuming we could actually compile it)
protocol Typographable {
    func setTypography(_ typography: Typography)
}

extension UILabel : Typographable {

    func setTypography(_ typography: Typography) {

        self.font = typography.font
        self.textColor = typography.textColor
        self.textAlignment = typography.textAlignment
        self.numberOfLines = typography.numberOfLines
    }
}
(C) -> Void
<Self : C>(Self) -> Void

在技术细节方面,允许通过见证(一致性实现)thunks传播隐式
Self
参数。它通过向约束到一致类的thunk添加一个通用占位符来实现这一点

对于这样的一致性:

class C {}
class D : C {}

protocol P {
  func copy() -> Self
}

extension P where Self == C {
  func copy() -> C {
    return C()
  }
}

extension C : P {}

let d: P = D()
print(d.copy()) // C (assuming we could actually compile it)
protocol Typographable {
    func setTypography(_ typography: Typography)
}

extension UILabel : Typographable {

    func setTypography(_ typography: Typography) {

        self.font = typography.font
        self.textColor = typography.textColor
        self.textAlignment = typography.textAlignment
        self.numberOfLines = typography.numberOfLines
    }
}
(C) -> Void
<Self : C>(Self) -> Void
在Swift 4.0.3中,
C
的协议见证表(我有可能有助于理解它们)包含一个thunk条目,该条目具有以下签名:

class C {}

protocol P {
  func foo()
}

extension P {
  func foo() {}
}

extension C : P {}
(请注意,在我链接到的ramble中,我跳过了thunks的细节,只说PWT包含一个用于满足需求的实现条目。不过,在大多数情况下,语义是相同的)

然而,在Swift 4.1中,thunk的签名现在看起来如下:

class C {}
class D : C {}

protocol P {
  func copy() -> Self
}

extension P where Self == C {
  func copy() -> C {
    return C()
  }
}

extension C : P {}

let d: P = D()
print(d.copy()) // C (assuming we could actually compile it)
protocol Typographable {
    func setTypography(_ typography: Typography)
}

extension UILabel : Typographable {

    func setTypography(_ typography: Typography) {

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

但是,如果我们有以下扩展:

extension P where Self == C {
  func foo() {}
}
一切又恢复正常,因为两个签名现在都是
(Self)->Void

但是需要注意的一件有趣的事情是,当需求包含关联类型时,会保留旧的thunk签名。所以这是可行的:

extension P where Self : C {
  func foo() {}
}

但你可能不应该求助于这种可怕的变通方法。只需将协议扩展约束更改为
where Self:C

为什么不简单地
extension-UILabel:typographible{…}
@MartinR,因为我有另一个
XXLabel
,它具有不同的
setTypography
实现。如果我使用
扩展UILabel:typographible{…}
我必须写一些类似
如果self是XXLabel
的东西,这并不优雅。我目前没有解释(我无法验证在Swift 4.0中行为是否不同),但是
扩展typographible where self:UILabel{…}
将使其可编译。我可以确认您的方法适用于Swift 4(Xcode 9.2),但不适用于Swift 4.1。@JIEWANG是否继承自
UILabel
?在这种情况下,请注意,当您将
UILabel
typographible
一致时,动态调度到
settypographic
(在协议类型值上调用时)将调度到
UILabel
扩展,而不是
typographible
扩展。因此,您的方法在Swift 4.0.3中也不会完全起作用。