调度到 Swift 协议扩展

Dispatching to Swift protocol extension

为什么以下代码在#2 处打印“BaseP”?

protocol BaseP { func foo() }
extension BaseP { func foo() { print("BaseP") } }
protocol SubP: BaseP {}
extension SubP { func foo() { print("SubP") } }

class C: SubP {}
let subP1: SubP = C()
subP1.foo()   //  #1 prints "SubP", fine.

class BaseC: BaseP {}
class SubC: BaseC, SubP {}
let subP2: SubP = SubC()
subP2.foo()   //  #2 prints "BaseP". why?

    

在这两种情况下,我们都在静态类型为 SubP 的引用上调用 foo(), 引用具有符合 SubP 的 class 动态类型的对象。 即使它是静态调度,我认为它仍然应该调用 SubP.foo()。 为什么在 #2 调用基本协议实现?

此外,为什么从 BaseC 继承会有所不同 (如果在 class SubC 行中我删除了 BaseC 或将其替换为 BaseP, 然后它突然打印出 "SubP")?

当你调用subP1.foo()subP2.foo()时,你调用的是BaseP中满足协议要求foofoo。 (即 见证 BaseP.foo 的方法)。 这样的证人只能有一个。

另一个重要的是foo要求不在SubP中,而是在BaseP中。 SubP 的唯一要求是符合者也必须符合 BaseP

subP1的情况下,C直接符合SubP。要解决 SubP 的唯一要求,C 也必须符合 BaseP。现在编译器需要确定哪个方法可以见证 foo。有两种方法可用,但是 SubP 扩展中的一种隐藏了 BaseP 扩展中的一种,因此选择 SubP 扩展中的一种作为见证,所以你看SubP 已打印。

subP2的情况下,foo的witness在你让BaseC符合BaseP的时候就已经确定了。在这里,只有一个选择 - BaseC 扩展中的 foo。当你后来让 SubC 符合 SubP 时,唯一剩下的要求就是 SubC 也应该符合 BaseP。好吧,你说 SubC 继承自 BaseC,所以没关系。编译器很高兴,BaseP 扩展中的 foo 最终成为见证人。