13

Can someone explain why @objc keyword is needed here to compile the code?

As I understood this keyword is used in order to work ObjC message method dispatch. But this is not an NSObject instance.

 class MyClass {
 }

 extension MyClass {
     @objc func extensionMethod() { /// THIS LINE
         print("A")
     }
 }

 class SubClass: MyClass {
     override func extensionMethod() {
         print("B")
     }
 }

Does @objc keyword enable message dispatch as well as dynamic? Or not?

BaseZen
  • 8,650
  • 3
  • 35
  • 47
Vyacheslav
  • 26,359
  • 19
  • 112
  • 194

1 Answers1

17

Does @objc keyword enable message dispatch as well as dynamic?

Not usually. Usually, the @objc attribute on its own just exposes a given class member to Objective-C – Swift is still free to dispatch to it either using table or static dispatch. You would need to mark the member as dynamic if you wanted Swift to use message dispatch when calling it.

However, for a non-final @objc class extension member, Swift will automatically infer it to be dynamic. Why? Because for interoperability reasons, Swift allows @objc extension members to override and be overridden (much like how you can override an Obj-C method in a subclass category). In order to achieve this behaviour, Swift relies on Obj-C message dispatch.

Therefore, in an extension, @objc infers dynamic. You cannot override an extension member without exposing it to the Obj-C runtime because extension members cannot currently be added to Swift class vtables (as Swift vtables currently cannot have members dynamically added to them at runtime).

But this is not an NSObject instance.

On Apple platforms (i.e those with Obj-C interop), all Swift classes are exposed to the Obj-C runtime, and all implicitly inherit from a special Obj-C base class called _SwiftObject, which conforms to NSObjectProtocol. So Swift classes are able to take advantage of message dispatch without having to inherit from NSObject.

Hamish
  • 78,605
  • 19
  • 187
  • 280
  • It seems this is a very nice explanation. But could you provide some proofs? – Vyacheslav May 03 '18 at 11:21
  • @Vyacheslav Unfortunately, I don't believe much of this is officially documented – you have to dig into the compiler/stdlib code to see it, for example [here's where `dynamic` inference takes place](https://github.com/apple/swift/blob/01312d9623357672bf1a72215ed88a21590f02c5/lib/Sema/TypeCheckDecl.cpp#L2590), [here's where `_SwiftObject` is declared](https://github.com/apple/swift/blob/01312d9623357672bf1a72215ed88a21590f02c5/stdlib/public/runtime/SwiftObject.h). – Hamish May 03 '18 at 11:25
  • what do you mean 'apple platform'? How will this code behave in e.g. Ubuntu? – Vyacheslav May 03 '18 at 12:33
  • @Vyacheslav On Linux, there's no Obj-C interop, so Swift classes won't inherit from `_SwiftObject`, you won't be able to mark things as `@objc` or `dynamic`, and you won't be able to override extension members. – Hamish May 03 '18 at 12:35
  • @Vyacheslav No, selectors are a mechanism used to refer to Obj-C exposed methods, so they won't work without Obj-C interoperability. – Hamish May 03 '18 at 12:44
  • @Vyacheslav Happy to be of help :) – Hamish May 03 '18 at 12:45
  • @Vyacheslav if you look in lldb on a layout of a "pure" swift class than you will see it has a "isa" pointer and the memory layout is basically the same as for the ObjC class. The same memory layout "appears" when a Swift class inherits from NSObject. – sloik May 03 '18 at 14:32
  • Sorry to bother. I read this, but still not sure if my implementation [here](https://stackoverflow.com/questions/50727968/how-to-modify-uibuttons-accessibilitylabel-in-an-extension/50820671#50820671) would work 100%. My question is about **Swift 3**. Can you just confirm if that if I add `@objc open override var accessibilityLabel: String?{...` would it work with no doubt?! – mfaani Jun 12 '18 at 15:36
  • 1
    @Honey No bother, always happy to be pinged with questions :) I wouldn't rely on defining `@objc open override var accessibilityLabel: String?` in a `UIButton` extension, as if `UIButton` adds its own override of `accessibilityLabel`, there's no guarantee that your implementation will be called (in the same way that if you define the same method in two categories of an Obj-C class, there's no guarantee on which implementation will be called). It's only reliable if you can guarantee that there's one implementation for a given class (such as in OP's example as they're overriding in a subclass). – Hamish Jun 12 '18 at 19:14
  • thanks. So what part of that logic has changed in Swift4? – mfaani Jun 14 '18 at 13:51
  • 2
    @Honey None, as far as I’m aware. The only thing that has changed in Swift 4 is the fact that Swift no longer infers the `@objc` attribute on members of `NSObject` inheriting classes. OP’s example therefore would have compiled in Swift 3 without the explicit `@objc` if `MyClass` inherited from `NSObject` (as the compiler would have inferred the `@objc`). But now the compiler requires an explicit `@objc` to make it clear you’re exposing it to Obj-C, regardless of `NSObject` inheritance. – Hamish Jun 14 '18 at 13:58
  • This is an old question, but what if you add the keyword `final` to the override method. Does that make it static dispatch? – David James Apr 10 '22 at 06:45