调用 Swift 协议扩展方法而不是子类中实现的方法 [英] Swift protocol extension method is called instead of method implemented in subclass

查看:86
本文介绍了调用 Swift 协议扩展方法而不是子类中实现的方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我遇到了以下代码(Swift 3.1)中解释的问题:

I've encountered a problem that is explained in the code below (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() 调用按预期工作.

So I would expect that "SubClass methodA" text should be printed after object1.methodB() call. But for some reason default implementation of methodA() from protocol extension is called. However object2.methodB()call works as expected.

这是协议方法调度中的另一个 Swift 错误,还是我遗漏了什么并且代码正常工作?

Is it another Swift bug in protocol method dispatching or am I missing something and the code works correctly?

推荐答案

这就是协议当前调度方法的方式.

This is just how protocols currently dispatch methods.

协议见证表(有关详细信息,请参阅此 WWDC 演讲)为了在协议类型的实例上被调用时动态分派到协议要求的实现.它只是一个函数实现的列表,用于为给定的符合类型调用协议的每个要求.

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.

声明其符合协议的每种类型都有自己的协议见证表.您会注意到我说的是说明其符合性",而不仅仅是符合".BaseClass 获取自己的协议见证表以符合 MyProtocol.然而,SubClass 并没有获得自己的表格以符合MyProtocol——相反,它仅仅依赖于BaseClass.如果您将
: MyProtocol 移到 SubClass 的定义中,它将拥有自己的 PWT.

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 to MyProtocol. However SubClass does not get its own table for conformance to MyProtocol – instead, it simply relies on BaseClass's. If you moved the
: MyProtocol down to the definition of SubClass, it would get to have its own PWT.

所以我们在这里只需要考虑 BaseClass 的 PWT 是什么样子的.好吧,它没有为协议要求 methodA()methodB() 中的任何一个提供实现——因此它依赖于协议扩展中的实现.这意味着符合 MyProtocolBaseClass 的 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 requirements methodA() or methodB() – so it relies on the implementations in the protocol extension. What this means is that the PWT for BaseClass conforming to MyProtocol just contains mappings to the extension methods.

因此,当扩展 methodB() 方法被调用,并调用 methodA() 时,它会通过 PWT 动态分派该调用(因为它是在协议类型的实例上调用;即 self).因此,当 SubClass 实例发生这种情况时,我们将通过 BaseClass 的 PWT.所以我们最终调用了 methodA() 的扩展实现,不管 SubClass 是否提供了它的实现.

So, when the extension methodB() method is called, and makes the call out to methodA(), it dynamically dispatches that call through the PWT (as it's being called on a protocol-typed instance; namely self). So when this happens with a SubClass instance, we're going through BaseClass's PWT. So we end up calling the extension implementation of methodA(), regardless of the fact that SubClass provides an implementation of it.

现在让我们考虑 JustClass 的 PWT.它提供了一个 methodA() 的实现,因此它的符合 MyProtocol 的 PWT 具有 那个 实现作为 methodA() 的映射,以及 methodB() 的扩展实现.因此,当 methodA() 通过其 PWT 动态分派时,我们最终会进入 实现.

Now let's consider the PWT of JustClass. It provides an implementation of methodA(), therefore its PWT for conformance to MyProtocol has that implementation as the mapping for methodA(), as well as the extension implementation for methodB(). So when methodA() is dynamically dispatched via its PWT, we end up in its implementation.

正如我在本问答中所说的,子类的这种行为没有为其超类的协议获取自己的 PWT(es) 符合确实有些令人惊讶,并且已被列为错误.正如 Swift 团队成员 Jordan Rose 在错误报告的评论中所说,其背后的原因是

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

[...] 子类不能提供新成员来满足一致性.这很重要,因为可以将协议添加到一个模块中的基类和另一个模块中创建的子类.

[...] The subclass does not get to provide new members to satisfy the conformance. This is important because a protocol can be added to a base class in one module and a subclass created in another module.

因此,如果这是行为,则已经编译的子类将缺少任何来自超类一致性的 PWT,而这些 PWT 在事后添加到另一个模块中,这将是有问题的.

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.

正如其他人已经说过的,这种情况下的一种解决方案是让 BaseClass 提供自己的 methodA() 实现.此方法现在将位于 BaseClass 的 PWT 中,而不是扩展方法.

As others have already said, one solution in this case is to have BaseClass provide its own implementation of methodA(). This method will now be in BaseClass's PWT, rather than the extension method.

当然,因为我们在这里处理,它不仅仅是BaseClass列出的方法的实现——而是一个 thunk 然后通过类的 vtable(类实现多态的机制)动态调度.因此,对于 SubClass 实例,我们最终会调用它对 methodA() 的覆盖.

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 a SubClass instance, we'll wind up calling its override of methodA().

这篇关于调用 Swift 协议扩展方法而不是子类中实现的方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆