Kind of nerd question. It's unclear to me, what exactly makes this code works:
class Shape { }
extension Shape {
@objc func redraw() {
print("from ext")
}
}
class Circle: Shape { }
class Line: Shape {
override func redraw() { // Compiler error: Declarations from extensions cannot be overridden yet
print("from subclass")
}
}
let line = Line()
let shape:Shape = line
let circle = Circle()
line.redraw() //from subclass
circle.redraw() //from ext
shape.redraw() //from subclass
If I omit @objc
keyword in extension, the code won't compile - it's expected behaviour since methods in extension use static method dispatch -> cannot be overridden.
But why adding @objc
makes it work? According to documentation and most articles, all is @objc
doing is making things visible to Objective-c runtime. To change method dispatch type there is a special keyword - dynamic
. But seems it is not necessary here!
Help me figure out, why is adding @objc
(and omitting dynamic
) makes such things possible.
Extension methods cannot be overridden the way classes and instance methods are. They are overridden by a slight trick in how the compiler selects which extension method to use by using "closeness" of the method to the caller via namespaces.
If a protocol defines a method, and provides a default implementation in an extension, a class can override that method, and the override will be called for any reference to the instance, even if the reference's type is declared as the protocol.
We use the override keyword to declare method overriding. For example, class Vehicle { func displayInfo(){ ... } } class Car: Vehicle { // override method override func displayInfo() { ... } }
The nonobjc attribute tells the compiler to make the declaration unavailable in Objective-C code, even though it's possible to represent it in Objective-C. Applying this attribute to an extension has the same effect as applying it to every member of that extension that isn't explicitly marked with the objc attribute.
Extensions,
as the name already says, are supposed to extend/add/include methods to an existing implementation, making them one of the most beautiful things about Objective-C, and now Swift, since you can add code to a class or framework you do not own. Therefore, it makes sense that you’re not supposed to “replace” code in extensions, conceptually speaking.
That’s why the compiler complains when you try to do it.
Also Check out this answer.
however this seems to be a support issue too, as swift compiler simply throw this error:
overriding non-@objc declarations from extensions is not supported.
According to Apple,
Extensions can add new functionality to a type, but they cannot override existing functionality.
But that is not the case, as we are overriding from the extension not vice versa,
which takes us back to the declaration of extension
.
Extensions add new functionality to an existing class, structure, enumeration, or protocol type. This includes the ability to extend types for which you do not have access to the original source code (known as retroactive modeling). Extensions are similar to categories in Objective-C. (Unlike Objective-C categories, Swift extensions do not have names.) Here.
Going back to the legacy topic swift compiler vs Objc compiler,
Dynamic dispatch vs. Static dispatch .
And there is no official documentation from apple on why this is not supported from the swift compiler or if they have any future plans to fix this or consider it an issue at all.
However, there’s no such thing as Swift dynamic dispatch; we only have the Objective-C runtime’s dynamic dispatch. That means you can’t have just dynamic and you must write @objc dynamic. So this is effectively the same situation as before, just made explicit.
And here is a great article talking about this topic deeply.
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