我遇到了以下代码(Swift 3.1)中解释的问题:
protocol MyProtocol {
func methodA()
func methodB()
}
extension MyProtocol {
func methodA() {
print("Default methodA")
}
func methodB() {
methodA()
}
}
// Test 1
class BaseClass: MyProtocol {
}
class SubClass: BaseClass {
func methodA() {
print("SubClass methodA")
}
}
let object1 = SubClass()
object1.methodB()
//
// Test 2
class JustClass: MyProtocol {
func methodA() {
print("JustClass methodA")
}
}
let object2 = JustClass()
object2.methodB()
//
// Output
// Default methodA
// JustClass methodA
因此,我希望在调用后应打印“ SubClass methodA”文本object1.methodB()
。但是由于某种原因,methodA()
调用了from协议扩展的默认实现。但是,object2.methodB()
呼叫按预期方式工作。
是协议方法调度中的另一个Swift错误,还是我丢失了一些东西并且代码正常工作?
这就是协议当前调度方法的方式。
使用协议见证表(有关更多信息,请参见WWDC谈话),以便在协议类型的实例上被调用时动态地调度到协议要求的实现。实际上,这只是功能实现的清单,以针对给定的一致性类型调用协议的每种要求。
声明其与协议符合性的每种类型都有其自己的协议见证表。您会注意到,我说的是“说明其一致性”,而不仅仅是“符合”。BaseClass
获得自己的协议见证表以符合MyProtocol
。但是,SubClass
并没有获得自己的表来符合MyProtocol
–相反,它仅依赖于BaseClass
。如果将
: MyProtocol
下移到的定义SubClass
,它将具有自己的PWT。
因此,我们仅需考虑的是PWT的BaseClass
外观。那么,它不会为任何的协议要求提供一个实现methodA()
或methodB()
-因此它依赖于该协议扩展的实现。这意味着BaseClass
符合的PWT MyProtocol
仅包含到扩展方法的映射。
因此,当methodB()
调用extension 方法并将其发出到时methodA()
,它会通过PWT动态分配该调用(因为它是在协议类型的实例上调用,即self
)。因此,当SubClass
实例发生这种情况时,我们将进行BaseClass
PWT。因此methodA()
,无论是否SubClass
提供扩展实现,我们最终都会调用的扩展实现。
现在让我们考虑的PWT JustClass
。它提供的实现methodA()
,因此,其对符合PWT MyProtocol
具有该实施作为映射methodA()
,以及对于扩展实现methodB()
。因此,当methodA()
通过其PWT动态调度时,我们最终实现了它。
正如我在本问答中所言,子类的这种行为并未为其超类所遵循的协议获取自己的PWT确实有些令人惊讶,并且已被记录为bug。正如Swift团队成员Jordan Rose在错误报告的评论中所说的那样,其背后的原因是
[...]子类无法提供新成员来满足一致性要求。这很重要,因为可以将协议添加到一个模块中的基类,而可以将其添加到另一个模块中。
因此,如果这是行为,那么已经编译的子类将缺少事实之后在另一个模块中添加的超类一致性的任何PWT,这将是有问题的。
正如其他人已经说过的,这种情况下的一种解决方案是BaseClass
提供自己的实现methodA()
。现在,此方法将位于BaseClass
的PWT中,而不是扩展方法中。
尽管当然,因为我们在这里处理类,所以它不仅仅是BaseClass
“列出的方法的实现”,而是一个笨拙的东西,然后通过该类的vtable动态调度(通过该类实现类的机制)多态性)。因此,对于一个SubClass
实例,我们最终将调用其的覆盖methodA()
。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句