为什么下面的协议有这个必需的功能?

Why does the following protocol have this required function?

WWDC 2015 中提到了以下代码:

protocol Drawable {
  func isEqualTo(other: Drawable) -> Bool
  func draw()
}

extension Drawable where Self : Equatable {
  func isEqualTo(other: Drawable) -> Bool {
    if let o = other as? Self { return self == o }
    return false
  } 
}

我对整个协议扩展的事情有点困惑。为什么他们会在 Drawable 协议中包含 isEqualTo(other: Drawable) -> Bool,然后仅在 self 可等式时才扩展?为什么 isEqualTo 应该是所有 Drawable 对象的必需方法?在我看来,如果一个新的 class/struct 还没有实现 Equatable,那么这些对象就没有能力进行逻辑上的相等性检查,所以它们 不能 [=21] =] 实施一个平等的方法。我认为将其作为可选实现会更有意义。我的逻辑错在哪里?

我只能猜测为什么 isEqualTo: 是可绘制协议的必需方法。也许这样不管画这些东西的人都不会浪费时间画同样的东西两次?

不过我可以评论其余部分。

Equatable 是一个 Swift 协议(在 Objective-C 中不可用),它要求为该类型定义一个 == 运算符。

在Objective-C中,没有运算符重载。而且,在Objective-C中,使用==来比较对象只是比较它们的指针。如果对象是相同的对象(相同的内存位置),则 == returns 为真。如果我们想查看对象是否是不同的对象但仍然被认为是相等的,我们必须使用 isEqualTo:。这是由 NSObject 定义的方法,默认实现只是 returns == 比较的结果。但是 classes 倾向于覆盖它。

在 Swift 中,== 有不同的行为。在 Swift 中,== returns 的行为与我们期望 isEqualTo: 方法在 Objective-C 中的行为相似。这是因为 Swift 具有用于比较引用的 === 运算符。 === returns 如果这些对象 相同 (相同的内存位置),则为真,但 == 是自定义实现的方法,用于确定对象是否即使它们位于不同的内存位置,也被视为相等。

所以我猜 Drawable 协议在将 isEqualTo: 声明为其必需方法之一时考虑到了 Objective-C classes。

我们也可以这样编写 Drawable 协议:

protocol Drawable: Equatable {
    func draw()
}

从严格的 Swift 角度来看,这是一个大致等效的协议定义。但这意味着使用 Drawable 对象的任何人都希望将它们与 Swift 中的 == 而不是 isEqualTo: 进行比较。而且,这意味着如果我们想使用协议中的任何 Objective-C 定义的对象,现在我们必须为每个对象实现一个自定义的 == 函数,这很可能如下所示:

func == (left ObjCClass, right ObjCClass) -> Bool {
    return left.isEqualTo(right)
}

但是我们必须为每个 Objective-C class 做这个我们想定义为Drawable.

另一种方法是使用 isEqualTo: 一种非常常见的 Objective-C 方法,按照您介绍的方式定义我们的协议。

现在,要使我们所有的 Swift 类型都符合 Drawable,我们所要做的就是实现 draw() 方法并符合 Equatable。只要我们符合 Equatable,扩展就会将 isEqualTo: 方法作为简单的 return left == right 有效地添加到我们的 Swift 类型中(并且存在 == 方法是 Equatable 协议保证的。

正在解决的问题是泛型的限制。

假设我们有一个 Bird 结构体和一个 Insect 结构体。通用等值表让我们定义 == ,其中实际对象类型是相同的类型。所以我们可以让 Bird 采用 Equatable 这样如果我们有 b1b2 都键入为 Bird 我们可以判断它们是否相等。我们可以让 Insect 采用 Equatable,这样如果我们 i1i2 都输入为 Insect,我们就可以判断它们是否相等。

但是现在假设Bird和Insect都采用了Flier协议。您不能 让 Flier 采用 Equatable,因为泛型的工作方式存在限制。因此,如果两个对象的类型都为 Flier,则无法为它们实现等同性。

该视频演示了协议扩展解决了这个问题。使用 Flier 上的协议扩展,您可以定义一个 different 方法来比较两个 Fliers 并确定它们是否相等 - 即首先确定它们是否相同 class然后应用 ==。因此,您可以理解 Flier(一种协议)的等同性。