Swift Bits: @dynamic vs @objc
Dispatch caveats
You have probably, at some point, become aware of dispatch methods:
Static
Dynamic
Message dispatch
Explaining each of them has been done many times and continues to bring new insights. Here are some valuable links to help you get the full picture:
What’s the Difference?
Both of them come from the Objective-C world when we had only Message Dispatch using objc_msgSend.
@objcexposes things to the Objective-C runtime, but by itself does not always force Objective-C message dispatch for Swift callers.
Swift adds @objc implicitly when:
You subclass
NSObjectYou override an Objective-C method
You conform to an
@objcprotocol
In other cases, behavior varies depending on where it’s added.
For a property:
class Person: NSObject {
@objc var name: String = “”
}What this does:
The getter and setter are exposed as Objective-C methods:
- (NSString *)name
- (void)setName:(NSString *)name
The property becomes visible to:
Objective-C code (normal property access)
KVC (
value(forKey: “name”),setValue(_:forKey:))
For a class:
@objc class MyController: NSObject {
@objc func doStuff() { }
}What this does:
Registers the class with the Objective-C runtime (as long as it’s Obj-C compatible, typically via
NSObject).The class is now visible in Objective-C:
MyController *c = [[MyController alloc] init];Can be used in Objective-C APIs, storyboards, etc.
Important points:
Marking a class with
@objc does not automatically make all members@objc. You must either:Mark members individually with
@objc, orUse
@objcMemberson the class:@objcMembers class MyController: NSObject { func foo() { } // implicitly @objc }
dynamic(usually used as@objc dynamic) is what forces Objective-C–style message dispatch even from Swift.
In Swift, this forces dynamic dispatch. And that’s something confusing for Objective-C old-timers.
Tricky Story
If you remember @dynamic from Objective-C, it will NOT generate getter/setter and it says: “You will find out the implementation later, at runtime“.
In Swift, @NSManaged is used to postpone getter/setter generation:
class Person: NSManagedObject {
@NSManaged var name: String
}Summary
@objc:Makes the symbol participate in the Objective-C runtime (gives it a selector, etc.).
Ensures Objective-C callers use message dispatch.
Does not forbid the Swift compiler from using static/vtable dispatch for Swift call sites.
dynamic(which implies@objc):Does force the use of Objective-C dynamic dispatch (Message Dispatch) (via
objc_msgSend) for all calls, including from Swift.This is what you need for KVO, method swizzling, and other runtime features.
If you want a simple rule of thumb:

