Swift Protocol Extension Method Is Called Instead of Method Implemented in Subclass

Swift protocol extension method is called instead of method implemented in subclass

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().

Swift protocol extension method dispatch with superclass and subclass

From the post The Ghost of Swift Bugs Future, here are the rules for dispatch for protocol extensions that are mentioned at the end of the post.

  1. IF the inferred type of a variable is the protocol:
  2. AND the method is defined in the original protocol THEN the
    runtime type’s implementation is called, irrespective of whether
    there is a default implementation in the extension.
  3. AND the method is not defined in the original protocol, THEN the
    default implementation is called.
  4. ELSE IF the inferred type of the variable is the type THEN the type’s implementation is called.

So in your condition, you're saying that method1() is defined in the protocol and it has been implemented in the subclass. But your superclass is adopting the protocol but it is not implementing the method1() and subclass just inherits from the Superclass and doesn't adopt to the protocols directly. That's why I believe that is the reason when you call foo.method1(), it doesn't invoke the the subclass implementation as stated by point 1 & 2.

But when you do,

class SomeSuperclass: TheProtocol {
func method1(){
print("super class implementation of method1()")}
}

class MyClass : SomeSuperclass {

override func method1() {
print("Called method1 from MyClass implementation")
}

override func method2NotInProtocol() {
print("Called method2NotInProtocol from MyClass implementation")
}
}

and then when you call,

 let foo: TheProtocol = MyClass()
foo.method1() // Called method1 from MyClass implementation
foo.method2NotInProtocol()

So what could be the workaround for this bug (which seems to be a bug) is that, you need to implement the protocol method in the superclass and then you need to override the protocol method in the sub class. HTH

Swift protocol and extension, I need to call overridden method or default extension method as per requirement

Hey I got the answers that is to conform the protocol by the object call your default method rather than overriddedn, so we can call both defination as required

let honda: Vehicle = Car()
honda.Drive()
honda.Stop()
// Output
// Can Drive
// iiiich..

When we create a variable without type then this is static dispatch when a object conform a protocol only.

Swift 2 protocol extension not calling overridden method correctly

Unfortunately protocols don't have such an dynamic behavior (yet).

But you can do that (with the help of classes) by implementing commonBehavior() in the ParentClass and overriding it in the ChildClass. You also need CommonThing or another class to conform to CommonTrait which is then the superclass of ParentClass:

class CommonThing: CommonTrait {
func say() -> String {
return "override this"
}
}

class ParentClass: CommonThing {
func commonBehavior() -> String {
// calling the protocol extension indirectly from the superclass
return (self as CommonThing).commonBehavior()
}

override func say() -> String {
// if called from ChildClass the overridden function gets called instead
return commonBehavior()
}
}

class AnotherParentClass: CommonThing {
override func say() -> String {
return commonBehavior()
}
}

class ChildClass: ParentClass {
override func say() -> String {
return super.say()
}

// explicitly override the function
override func commonBehavior() -> String {
return "from child class"
}
}
let parent = ParentClass()
parentClass.say() // "from protocol extension"
let child = ChildClass()
child.say() // "from child class"

Since this is only a short solution for your problem I hope it fits in your project.

Swift Protocol Extensions overriding

The short answer is that protocol extensions don't do class polymorphism. This makes a certain sense, because a protocol can be adopted by a struct or enum, and because we wouldn't want the mere adoption of a protocol to introduce dynamic dispatch where it isn't necessary.

Thus, in getColor(), the color instance variable (which may be more accurately written as self.color) doesn't mean what you think it does, because you are thinking class-polymorphically and the protocol is not. So this works:

let colorB = B().color // is "Red color" - OK

...because you are asking a class to resolve color, but this doesn't do what you expect:

let b = B().getColor() // is "Default color" BUT I want it to be "Red color"

...because the getColor method is defined entirely in a protocol extension. You can fix the problem by redefining getColor in B:

class B: A, RedColor {
func getColor() -> String {
return self.color
}
}

Now the class's getColor is called, and it has a polymorphic idea of what self is.

Swift protocol and extension, I need to call instance method if it present

It all depends on two things:

  • Is this a requirement of the protocol or merely injected by an extension?
  • Is the receiver typed as Foo or Bar?

So for example:

protocol Foo {
// func test() // note we've commented this out!
}
extension Foo {
func test() {
print("foo")
}
}
class Bar: Foo {
func test() {
print("bar")
}
}
let myBar = Bar()
myBar.test() // bar
let myFoo : Foo = Bar()
myFoo.test() // foo

But if you uncomment the line that I commented out, now you always get "bar" printed.



Related Topics



Leave a reply



Submit