6

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.

rmaddy
  • 314,917
  • 42
  • 532
  • 579
surfrider
  • 1,376
  • 14
  • 29
  • I'm tempted to dupe this to https://stackoverflow.com/questions/50151102/objc-keyword-extension-subclass-behaviour/50153780#50153780. Do you believe that question and answer matches this one close enough that future searchers should be directed over there, or do the answers here give more detail? – Rob Napier Feb 24 '19 at 15:42
  • @RobNapier Unfortunately I did not find this before created topic. Questions are close enough. Answer from that topic is very explainable. But Mohmmad S gave some awesome links i'm diving into now. I want to combine his answer and that topic :) – surfrider Feb 24 '19 at 16:16

1 Answers1

3

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.

Mohmmad S
  • 5,001
  • 4
  • 18
  • 50
  • I'd like to draw more attention to your linked answer from Hamish: https://stackoverflow.com/questions/50151102/objc-keyword-extension-subclass-behaviour/50153780#50153780. The key insight to this question is "However, for a non-final @objc class extension member, Swift will automatically infer it to be dynamic" – Rob Napier Feb 24 '19 at 15:41
  • The first SO link from your post is answered to my question. Thank you! Swift has a very VERY non-obvious behaviour... – surfrider Feb 24 '19 at 16:18