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,但是找不到任何有用的东西.
这是正常的,我错过了什么吗?
这很有趣。长话短说(好吧,也许没那么短) \xe2\x80\x93 这是#12174的有意副作用,它允许协议扩展方法返回以满足非最终类的协议要求,这意味着您现在可以在 4.1 中这样说:Self
\n\n\nRun Code Online (Sandbox Code Playgroud)\nprotocol 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
在 Swift 4.0.3 中,您会在扩展实现上遇到一个令人困惑的错误f()
:
\n\n\n\n
f()
非最终类\'C\'中的方法\' \'必须返回Self
以符合协议\'P
\'
这如何应用于您的示例?好吧,考虑一下这个有点相似的例子:
\n\nclass 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 使一致性变得非法(为了使第一个示例中的一致性合法),并且无论是否有回报,它都会这样做。C
D
Self
我们实际上想要用扩展来表达的是Self
必须是或继承自 C
,这迫使我们考虑子类使用一致性时的情况。
在你的例子中,它看起来像这样:
\n\nprotocol 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\nprotocol 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在技术细节上,#12174的作用是允许通过见证(一致实现)thunk 传播隐式Self
参数。它通过向受限于一致类的 thunk 添加通用占位符来实现此目的。
因此,对于这样的一致性:
\n\nclass 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 条目:
(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\nextension 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\nextension P where Self : C {\n func foo() {}\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n一切都恢复正常了,因为两个签名现在都是<Self : C>(Self) -> Void
。
不过,关于#12174需要注意的一件有趣的事情是,当需求包含关联类型时,它会保留旧的 thunk 签名。所以这有效:
\n\nclass 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
.
归档时间: |
|
查看次数: |
580 次 |
最近记录: |