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 extension not being called
You are trying to make a Swift protocol extension on an Objective-C protocol. That's never going to work, because Objective-C cannot see a Swift protocol extension, so Cocoa (which is Objective-C) will never know about your implementation and will never call it.
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 override protocol methods in sub classes
Use protocol extension with where clause. It works.
But I would not recommend you to have such things in your codebase.
class BaseViewController: UIViewController {
}
extension OptionsDelegate where Self: BaseViewController {
func handleSortAndFilter(opt: Options) {
print("Base class implementation")
}
}
extension BaseViewController: OptionsDelegate {
}
class InsipartionsViewController: BaseViewController {
}
extension OptionsDelegate where Self: InsipartionsViewController {
func handleSortAndFilter(opt: Options) {
print("Inspirations class implementation")
}
}
Swift overriding extension method
Just move the declaration of create
into the actual class declaration. Everything else can stay the same:
protocol Creator {
func create()
}
class BaseClass {
func create() {
print("create")
}
}
extension BaseClass: Creator {
}
// and so on
The effect is precisely the same, but now override func create()
is legal in your subclasses.
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()
.
Overriding methods in Swift extensions
Extensions cannot/should not override.
It is not possible to override functionality (like properties or methods) in extensions as documented in Apple's Swift Guide.
Extensions can add new functionality to a type, but they cannot override existing functionality.
Swift Developer Guide
The compiler is allowing you to override in the extension for compatibility with Objective-C. But it's actually violating the language directive.
That just reminded me of Isaac Asimov's "Three Laws of Robotics" /p>
Extensions (syntactic sugar) define independent methods that receive their own arguments. The function that is called for i.e. layoutSubviews
depends on the context the compiler knows about when the code is compiled. UIView inherits from UIResponder which inherits from NSObject so the override in the extension is permitted but should not be.
So there's nothing wrong with grouping but you should override in the class not in the extension.
Directive Notes
You can only override
a superclass method i.e. load()
initialize()
in an extension of a subclass if the method is Objective-C compatible.
Therefore we can take a look at why it is allowing you to compile using layoutSubviews
.
All Swift apps execute inside the Objective-C runtime except for when using pure Swift-only frameworks which allow for a Swift-only runtime.
As we found out the Objective-C runtime generally calls two class main methods load()
and initialize()
automatically when initializing classes in your app’s processes.
Regarding the dynamic
modifier
From the Apple Developer Library (archive.org)
You can use the dynamic
modifier to require that access to members be dynamically dispatched through the Objective-C runtime.
When Swift APIs are imported by the Objective-C runtime, there are no guarantees of dynamic dispatch for properties, methods, subscripts, or initializers. The Swift compiler may still devirtualize or inline member access to optimize the performance of your code, bypassing the Objective-C runtime. /p>
So dynamic
can be applied to your layoutSubviews
-> UIView Class
since it’s represented by Objective-C and access to that member is always used using the Objective-C runtime.
That's why the compiler allowing you to use override
and dynamic
.
Issue with the call hierarchy of a swift protocol extension
Overriding superclass methods in extensions: only allowed for Objective-C compatible methods
First of all we note that you can only override a superclass method in an extension of a subclass if the method is Objective-C compatible. For classes deriving from NSObject
, this is true for all instance methods (which is true here, as UIViewController
derives from NSObject
). This is covered in e.g. the following Q&A:
- Can you override between extensions in Swift or not? (Compiler seems confused!)
Enforcing dynamic dispatch via Objective-C runtime
Now, from Interoperability - Interacting with Objective-C APIs - Requiring Dynamic Dispatch, we note the following
When Swift APIs are imported by the Objective-C runtime, there are no
guarantees of dynamic dispatch for properties, methods, subscripts, or
initializers. The Swift compiler may still devirtualize or inline
member access to optimize the performance of your code, bypassing the
Objective-C runtime.You can use the
dynamic
modifier to require that access to members be
dynamically dispatched through the Objective-C runtime.
Moreover, from The Language Ref. - Declarations - Declaration Modifyers we read
dynamic
(modifier)Apply this modifier to any member of a class that can be represented
by Objective-C. When you mark a member declaration with thedynamic
modifier, access to that member is always dynamically dispatched using
the Objective-C runtime. Access to that member is never inlined or
devirtualized by the compiler.Because declarations marked with the
dynamic
modifier are dispatched
using the Objective-C runtime, they’re implicitly marked with theobjc
attribute.
Enforcing dynamic dispatch for reachabilityDidChange(...)
in A
Hence, if you add the dynamic
modifier to method reachabilityDidChange(...)
in your superclass A
, then access to reachabilityDidChange(...)
will always be dynamically dispatched using the Objective-C runtime, and hence find and make use of the correct overridden reachabilityDidChange(...)
method in the class B
extension, for instances of B
. Hence,
dynamic func reachabilityDidChange(online: Bool) { ... }
in A
will fix the above.
Below follows a more minimal example of your issue above, redeemed by demanding dynamic dispatch via obj-c runtime for method foo()
in class A
(equivalent to your method reachabilityDidChange(...)
in class A
).
import UIKit
protocol Foo: class {
func foo()
}
extension Foo {
func foo() { print("default") }
}
class A: UIViewController, Foo {
dynamic func foo() { print("A") } // <-- note dynamic here
func bar() { self.foo() }
/* \
hence, foo() is dynamically dispatched here, and for
instances where "self === B()", this is correctly
resolved as the foo() in the extension to B */
}
class B : A { }
extension B {
override func foo() {
super.foo()
print("B")
}
}
let a = A()
a.bar() // A
let b = B()
b.bar() // A B
/* or, only "A" if not using dynamic dispatch */
Related Topics
Checking If an Array of Custom Objects Contain a Specific Custom Object
Why Should Not Directly Extend Uiview or Uiviewcontroller
How to Make Physics Bodies Stick to Nodes Anchor Points
How to Make the Memberwise Initialiser Public, by Default, for Structs in Swift
Set a Default Value for Uipickerview in Swift
Difference Between Using Objectidentifier() and '===' Operator
Converting Swift Array to Cfarray in Xcode 8 (Swift 3)
How to Open Your App in Settings iOS 11
Vertically Aligning Text in an Nstextfield Using Swift
How to Test Whether Generic Variable Is of Type Anyobject
How to Write a Function That Will Unwrap a Generic Property in Swift Assuming It Is an Optional Type
Global Function Sequence(State:Next:) and Type Inference