overriding method signatures in extensions seems to produce unpredictable results in certain cases. The following example demonstrates two different results with a similar pattern.
class A: UIViewController {
func doThing() {
print("dothing super class")
}
override func viewDidLoad() {
print("viewdidload superclass")
super.viewDidLoad()
}
}
class B: A { }
extension B {
override func doThing() {
print("dothing sub class")
super.doThing()
}
override func viewDidLoad() {
print("viewdidload subclass")
super.viewDidLoad()
}
}
let a: A = B()
a.doThing()
let vc: UIViewController = B()
vc.viewDidLoad()
This prints :
dothing super class
viewdidload subclass
viewdidload superclass
You can see this skips the B
's implementation of doThing
when it is cast as A
, however includes both implementations of viewDidLoad
when cast as UIViewController
. Is this the expected behavior? If so, what is the reason for this?
ENV: Xcode 7.3, Playground
The surprise here is that the compiler permits the override in the extension. This doesn't compile:
class A {
func doThing() {
print("dothing super class")
}
}
class B: A {
}
extension B {
override func doThing() { // error: declarations in extensions cannot override yet
print("dothing sub class")
super.doThing()
}
}
In your example, it appears that the compiler gives you a pass because A derives from NSObject — presumably in order to allow this class to interact with Objective-C. This does compile:
class A : NSObject {
func doThing() {
print("dothing super class")
}
}
class B: A {
}
extension B {
override func doThing() {
print("dothing sub class")
super.doThing()
}
}
My guess is that the fact you're allowed to do this override at all is itself possibly a bug. The docs say:
Extensions can add new functionality to a type, but they cannot override existing functionality.
And overriding is nowhere listed as one of the things an extension can do. So it seems like this should not compile. However, perhaps this is permitted deliberately for compatibility with Objective-C, as I said before. Either way, we are then exploring an edge case, and you have very nicely elicited its edginess.
In particular, the preceding code still doesn't cause dynamic dispatch to become operational. That's why you either have to declare doThing
as dynamic
, as suggested by @jtbandes, or put it in the actual class rather than the extension — if you want polymorphism to operate. Thus, this works the way you expect:
class A : NSObject {
dynamic func doThing() {
print("dothing super class")
}
}
class B: A {
}
extension B {
override func doThing() {
print("dothing sub class")
super.doThing()
}
}
And so does this:
class A : NSObject {
func doThing() {
print("dothing super class")
}
}
class B: A {
override func doThing() {
print("dothing sub class")
super.doThing()
}
}
My conclusion would be: Very nice example; submit it to Apple as a possible bug; and Don't Do That. Do your overriding in the class, not in the extension.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With