Dynamic method dispatch is the ability to call methods by name at runtime rather than at compile time. In Swift, this is possible through interaction with the Objective-C runtime via the modifiers @objc and dynamic, or through the use of the AnyObject protocol.
Mechanisms:
@objc — marks a method/property/class for export to the Objective-C runtime.dynamic — requires execution through Objective-C message dispatch, rather than through a static call (vtable).Limitations:
@objc.@objc attribute or marked through inheritance from NSObject.Example:
import Foundation class Person: NSObject { @objc dynamic func sayHello() -> String { return "Hello!" } } let person = Person() // Dynamic call via Selector (perform:): if person.responds(to: #selector(Person.sayHello)) { if let result = person.perform(#selector(Person.sayHello))?.takeUnretainedValue() as? String { print(result) // "Hello!" } } }
Similarly, functions can be called through AnyObject with optional chaining:
@objc protocol Greeter { @objc optional func greet() } class Robot: NSObject, Greeter { func greet() { print("Beep-beep!") } } let anyGreeter: AnyObject = Robot() anyGreeter.greet?() // "Beep-beep!"
Can a struct support dynamic method dispatch through @objc, dynamic, or AnyObject?
Answer: No, only classes inherited from NSObject, or classes/protocols with the @objc modifier can support such mechanisms. Structs and enums are not compatible with the Objective-C runtime, so they cannot be dynamic participants.
Story
In a project, an @objc protocol was used for a delegate. One developer removed the inheritance of the delegate class from NSObject, causing optional methods to become unavailable for calls through optional chaining. The architecture broke down, and tests began to fail constantly.
Story
In an attempt to use KVO (key-value observing) on a struct property, a developer marked it as dynamic. The code compiled but did not work as expected because structs do not support the Objective-C runtime. Dynamic reactions to changes did not work, leading to missed UI updates.
Story
In a class extension, the @objc modifier was forgotten for a function that was called through perform(_:). As a result, crashes occurred in production when trying to call an unlinked selector. The cause was hard to identify until the absence of the function's export to Objective-C was noticed.