SWIFT协议扩展方法是调用子类中实现的方法

发布于 2025-02-05 02:48:56 字数 850 浏览 1 评论 0 原文

我遇到了下面代码中解释的问题(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()呼叫按预期工作。

是协议方法中的另一个迅速错误,还是我缺少某些内容并且代码正常工作?

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

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.

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

转瞬即逝 2025-02-12 02:48:57

这就是协议当前派遣方法的方式。

协议证人表(请参阅此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 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.

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.

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.

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.

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.

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 of methodA(). This method will now be in BaseClass'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 a SubClass instance, we'll wind up calling its override of methodA().

帝王念 2025-02-12 02:48:57

一个朋友与我分享的一个很短的答案是:

只有声明一致性的类获得协议证人表

这意味着具有该功能的子类对设置协议证人表的设置没有影响。

协议证人仅在协议,扩展名和实现该协议的具体类之间的合同。

A very short answer that a friend shared with me was:

Only the class that declares the conformance gets a protocol witness table

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.

一指流沙 2025-02-12 02:48:57

好吧,我想子类方法A不是多态的,因为您不能将覆盖关键字放在其上,因为该类不知道该方法是在协议扩展中实现的,因此不允许您覆盖它。扩展方法可能是在运行时介绍您的实现,就像2个确切的类别方法相互胜过目标C中的行为。协议扩展,从而从中获得多态性行为。不利的一面是,您不能在此层中删除方法,因为没有本机对抽象类的支持(这实际上是您尝试使用协议扩展的方法)

protocol MyProtocol {
    func methodA()
    func methodB()
}

class MyProtocolClass: MyProtocol {
    func methodA() {
        print("Default methodA")
    }

    func methodB() {
        methodA()
    }
}

// Test 1
class BaseClass: MyProtocolClass {

}

class SubClass: BaseClass {
    override func methodA() {
        print("SubClass methodA")
    }
}


let object1 = SubClass()
object1.methodB()
//

// Test 2
class JustClass: MyProtocolClass {
    override func methodA() {
        print("JustClass methodA")
    }
}

let object2 = JustClass()
object2.methodB()
//
// Output
// SubClass methodA
// JustClass methodA

也有相关的答案: 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)

protocol MyProtocol {
    func methodA()
    func methodB()
}

class MyProtocolClass: MyProtocol {
    func methodA() {
        print("Default methodA")
    }

    func methodB() {
        methodA()
    }
}

// Test 1
class BaseClass: MyProtocolClass {

}

class SubClass: BaseClass {
    override func methodA() {
        print("SubClass methodA")
    }
}


let object1 = SubClass()
object1.methodB()
//

// Test 2
class JustClass: MyProtocolClass {
    override func methodA() {
        print("JustClass methodA")
    }
}

let object2 = JustClass()
object2.methodB()
//
// Output
// SubClass methodA
// JustClass methodA

Also relevante answer here: Swift Protocol Extensions overriding

南七夏 2025-02-12 02:48:57

在您的代码中,

let object1 = SubClass()
object1.methodB()

您是从 sub Class 的实例中调用Methodb的,但是子类没有任何名为 Methodb 的方法。但是,它的超级类, BASECLASS 符合 myProtocol ,具有 Methodb Methodb。

因此,它将从 myProtocal 调用 Methodb 。因此,它将在扩展MyProtocol 中执行 Methoda

要达到您期望的目标,您需要在 Baseclass 中实现 methoda ,并在 subclass 中覆盖它,就像

class BaseClass: MyProtocol {
    func methodA() {
        print("BaseClass methodA")
    }
}

class SubClass: BaseClass {
    override func methodA() {
        print("SubClass methodA")
    }
}

现在以下代码一样,输出将

//Output
//SubClass methodA
//JustClass methodA

变为方法可以达到您的期望,但我不确定建议使用这种代码结构。

In your code,

let object1 = SubClass()
object1.methodB()

You invoked methodB from an instance of SubClass, but SubClass does not have any method named methodB. However its super class, BaseClass conform to MyProtocol, which has a methodB methodB.

So, it will invoke the methodB from MyProtocal. Therefore it will execute the methodA in extesion MyProtocol.

To reach what you expect, you need implement methodA in BaseClass and override it in SubClass, like the following code

class BaseClass: MyProtocol {
    func methodA() {
        print("BaseClass methodA")
    }
}

class SubClass: BaseClass {
    override func methodA() {
        print("SubClass methodA")
    }
}

Now, output would become

//Output
//SubClass methodA
//JustClass methodA

Although the method can reach what you expect, but I'm not sure this kind of code struct is recommended.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文