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中也不会完全起作用。