【问题标题】:Swift protocol with "where Self" clause带有“where Self”子句的 Swift 协议
【发布时间】:2018-06-18 15:51:28
【问题描述】:

除了这个带有协议扩展的语法:

protocol P {}
extension P where Self : UIView {}

...我偶然发现您可以在协议本身上使用相同的 where 子句:

protocol P where Self : UIView {}

请注意,这与限制通用协议的 where 子句相同,并且 not 本身是否使 P 成为通用协议。

我的实验似乎表明这里只能使用冒号,冒号后面的东西必须是类或协议(可能是泛型的)。

我开始好奇:这怎么会逃过我的注意?所以我去寻找它何时出现的证据。在 Swift 3.0 中,前一种语法是合法的,但 不是 后者。在 Swift 3.3 中,两者都是合法的。所以后一种语法一定是在 Swift 3.2 之类的东西中悄悄引入的。我说“悄悄地”是因为我在发行说明中找不到任何关于它的信息。

第二种语法是干什么用的?看起来,它只是一种确保没有其他类型可以采用该协议的便捷方式吗? Swift 标头似乎没有使用它。

【问题讨论】:

  • 能够在协议声明中说出where Self : UIViewSE-0156 的意外结果——该功能本身尚未完全实现(实际上编译器应该在准备好之前拒绝该语法)。它目前周围有很多锋利的边缘,所以我现在要避开它——见stackoverflow.com/a/50647762/2976878
  • @Hamish 该评论足以成为答案,imo
  • @Alexander 嗯,它确实大部分重复了我在链接答案中已经说过的东西,但我想它更容易作为这个问题的答案......是的,好吧,我会写一个答案: )

标签: swift where swift-protocols


【解决方案1】:

对协议声明施加超类约束的能力(即能够定义protocol P where Self : C,其中C是类的类型)是
SE-0156的过早结果,语法应该在 Swift 4.x 中被拒绝,直到该功能被实现。尝试在 Swift 4.x can cause miscompilation and crashes 中使用此功能,因此在 Swift 5 之前我会避免使用它。

在 Swift 5 (Xcode 10.2) 中,该功能具有 now been implemented。来自the release notes

协议现在可以将它们的符合类型限制为那些 子类给定的类。支持两种等效形式:

protocol MyView: UIView { /*...*/ }
protocol MyView where Self: UIView { /*...*/ } 

Swift 4.2 接受了第二种形式,但并未完全实现 有时可能会在编译时或运行时崩溃。 (SR-5581) (38077232)

此语法在MyView 上放置了一个超类约束,它将符合类型的类型限制为继承自(或成为)UIView 的类型。此外,MyView 的用法在语义上等同于存在类(例如 UIView & MyView),因为您可以访问类的成员和协议对值的要求。

例如,扩展发行说明的示例:

protocol MyView : UIView {
  var foo: Int { get }
}

class C : MyView {} // error: 'P' requires that 'C' inherit from 'UIView'

class CustomView : UIView, MyView {
  var foo: Int = 0
}

// ...

let myView: MyView = CustomView(frame: .zero)

// We can access both `UIView` members on a `MyView` value
print(myView.backgroundColor as Any)

// ... and `MyView` members as usual.
print(myView.foo)

【讨论】:

  • protocol P : UIView {}我希望不会!
  • IMO 更改是有意义的——协议派生(即protocol P2 : P1)在语义上应该真正等同于Self 的约束,即protocol P2 where Self : P1。因此,如果您允许protocol P2 where Self : SomeClass,那么也允许protocol P2 : SomeClass 似乎是合乎逻辑的(话虽这么说,我很欣赏它很容易被误认为是继承)。
  • 无论如何protocol P where Self : C & Self: Decodable{}编辑: 没关系。我可以使用 typealias 将 C & Decodable 和约束 Self 组合到该别名类型...
  • @Honey 在 Swift 5 中,你会写成 protocol P where Self : C, Self : Decodable {}protocol P where Self : C & Decodable {}protocol P : C, Decodable {}。然而,在 Swift 4.x 中,正如我在回答中所讨论的那样,编译器不支持这样的构造。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-04-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多