SWIFT协议扩展方法是调用子类中实现的方法
我遇到了下面代码中解释的问题(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()
。但是 object2.methodb()
呼叫按预期工作。
是协议方法中的另一个迅速错误,还是我缺少某些内容并且代码正常工作?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
这就是协议当前派遣方法的方式。
协议证人表(请参阅此wwdc Talk 以获取更多信息)用于动态地使用派遣协议要求的实现,以协议类型的实例调用。实际上,实际上只是函数实现的列表,可以呼吁给定符合类型的协议的每个要求。
说明其符合协议的每种类型都会获得其自己的协议证人表。您会注意到我说“说明它的符合性”,而不仅仅是“符合”。
移动到
BASECLASS
获取自己的协议证人表,以符合myProtocol
。但是,子类
dis 不获取自己的表格,以符合myProtocol
- 而是,它只是依靠baseclass
' 。如果将子类
的定义,它将具有自己的PWT。因此,我们必须在这里考虑的只是
Baseclass
的PWT。好吧,它不能为任何一个协议要求methoda()
或methodb()
- 因此,它依赖于协议扩展中的实现。这意味着Baseclass
符合myProtocol
的PWT仅包含与扩展方法的映射。因此,当调用Extension
Methodb()
方法时,将调用到Methoda()
时,它动态派遣通过PWT来调用(因为它被调用了协议类型的实例;self
因此,当这种情况使用子类
实例发生时,我们将通过Baseclass
的PWT。因此,无论sub -Class
提供了它的实现,我们最终都会调用Methoda()
的扩展实现。现在,让我们考虑
JustClass
的PWT。它提供了methoda()
的实现,因此其pwt符合myProtocol
具有 实现作为methodagea()的映射
,以及Methodb()
的扩展实现。因此,当Methoda()
通过其PWT动态调度时,我们最终以 实现。正如我所说的在此Q& a 中,这种子类别的这种行为没有获得自己的PWTS的PWTS,该协议是他们的超级学(ES)(ES)(ES)(ES)符合确实有些令人惊讶,并且已经作为错误。正如斯威夫特团队成员乔丹·罗斯(Jordan Rose)在《错误报告》的评论中所说的那样,其背后的原因是
因此,如果这是行为,那么已经编译的子类将缺乏来自超级类符合的任何PWT,这些PWT在另一个模块中添加后添加的超类构符,这将是有问题的。
正如其他人已经说过的那样,在这种情况下,一种解决方案是让
baseclass
提供其自己的methoda()
的实现。现在,此方法将在Baseclass
的PWT中,而不是扩展方法。虽然当然,因为我们在这里处理 class ,所以它不仅是
baseclass
的实现所列出的方法 - 而是它将是 thunk 然后,通过类“ VTable”(类实现多态性的机制)进行动态分配。因此,对于子类
实例,我们将调用其Methoda()
的替代。This is just how protocols currently dispatch methods.
A protocol witness table (see this WWDC talk for more info) is used in order to dynamically dispatch to implementations of protocol requirements upon being called on a protocol-typed instance. All it is, is really just a listing of the function implementations to call for each requirement of the protocol for a given conforming type.
Each type that states its conformance to a protocol gets its own protocol witness table. You'll note that I said "states its conformance", and not just "conforms to".
BaseClass
gets its own protocol witness table for conformance toMyProtocol
. HoweverSubClass
does not get its own table for conformance toMyProtocol
– instead, it simply relies onBaseClass
's. If you moved the: MyProtocol
down to the definition ofSubClass
, it would get to have its own PWT.So all we have to think about here is what the PWT for
BaseClass
looks like. Well, it doesn't provide an implementation for either of the protocol requirementsmethodA()
ormethodB()
– so it relies on the implementations in the protocol extension. What this means is that the PWT forBaseClass
conforming toMyProtocol
just contains mappings to the extension methods.So, when the extension
methodB()
method is called, and makes the call out tomethodA()
, it dynamically dispatches that call through the PWT (as it's being called on a protocol-typed instance; namelyself
). So when this happens with aSubClass
instance, we're going throughBaseClass
's PWT. So we end up calling the extension implementation ofmethodA()
, regardless of the fact thatSubClass
provides an implementation of it.Now let's consider the PWT of
JustClass
. It provides an implementation ofmethodA()
, therefore its PWT for conformance toMyProtocol
has that implementation as the mapping formethodA()
, as well as the extension implementation formethodB()
. So whenmethodA()
is dynamically dispatched via its PWT, we end up in its implementation.As I say in this Q&A, this behaviour of subclasses not getting their own PWTs for protocols that their superclass(es) conform to is indeed somewhat surprising, and has been filed as a bug. The reasoning behind it, as Swift team member Jordan Rose says in the comments of the bug report, is
Therefore if this was the behaviour, already-compiled subclasses would lack any PWTs from superclass conformances that were added after the fact in another module, which would be problematic.
As others have already said, one solution in this case is to have
BaseClass
provide its own implementation ofmethodA()
. This method will now be inBaseClass
's PWT, rather than the extension method.Although of course, because we're dealing with classes here, it won't just be
BaseClass
's implementation of the method that's listed – instead it will be a thunk that then dynamically dispatches through the class' vtable (the mechanism by which classes achieve polymorphism). Therefore for aSubClass
instance, we'll wind up calling its override ofmethodA()
.一个朋友与我分享的一个很短的答案是:
这意味着具有该功能的子类对设置协议证人表的设置没有影响。
协议证人仅在协议,扩展名和实现该协议的具体类之间的合同。
A very short answer that a friend shared with me was:
Meaning a subclass having that function has no effect on how the protocol witness table is setup.
The protocol witness is a contract only between the protocol, it's extensions, and the concrete class that implements it.
好吧,我想子类方法A不是多态的,因为您不能将覆盖关键字放在其上,因为该类不知道该方法是在协议扩展中实现的,因此不允许您覆盖它。扩展方法可能是在运行时介绍您的实现,就像2个确切的类别方法相互胜过目标C中的行为。协议扩展,从而从中获得多态性行为。不利的一面是,您不能在此层中删除方法,因为没有本机对抽象类的支持(这实际上是您尝试使用协议扩展的方法)
也有相关的答案: swift协议扩展覆盖
Well I suppose the subclass method A is not polymorphic because you can't put the override keyword on it, since the class doesn't know the method is implemented in an extension of the protocol and thus doesn't let you override it. The extension method is probably stepping on your implementation in runtime, much like 2 exact category methods trump each other with undefined behavior in objective C. You can fix this behavior by adding another layer in your model and implementing the methods in a class rather than the protocol extension, thus getting polymorphic behavior out of them. The downside is that you cannot leave methods unimplemented in this layer, as there is no native support for abstract classes (which is really what you're trying to do with protocol extensions)
Also relevante answer here: Swift Protocol Extensions overriding
在您的代码中,
您是从
sub Class
的实例中调用Methodb的,但是子类
没有任何名为Methodb
的方法。但是,它的超级类,BASECLASS
符合myProtocol
,具有Methodb
Methodb。因此,它将从
myProtocal
调用Methodb
。因此,它将在扩展MyProtocol
中执行Methoda
。要达到您期望的目标,您需要在
Baseclass
中实现methoda
,并在subclass
中覆盖它,就像现在以下代码一样,输出将
变为方法可以达到您的期望,但我不确定建议使用这种代码结构。
In your code,
You invoked methodB from an instance of
SubClass
, butSubClass
does not have any method namedmethodB
. However its super class,BaseClass
conform toMyProtocol
, which has amethodB
methodB.So, it will invoke the
methodB
fromMyProtocal
. Therefore it will execute themethodA
inextesion MyProtocol
.To reach what you expect, you need implement
methodA
inBaseClass
and override it inSubClass
, like the following codeNow, output would become
Although the method can reach what you expect, but I'm not sure this kind of code struct is recommended.