Der dynamische Methodenaufruf ist die Möglichkeit, Methoden zur Laufzeit (runtime) nach Namen aufzurufen, anstatt zur Compile-Zeit. In Swift ist dies durch die Interaktion mit der Objective-C-Laufzeit möglich, dank der Modifikatoren @objc und dynamic, oder bei Verwendung des Protokolls AnyObject.
Mechanismen:
@objc — markiert die Methode/Eigenschaft/Klasse für den Export in die Objective-C-Laufzeit.dynamic — erfordert die Ausführung über die Objective-C-Nachrichtenzustellung und nicht über einen statischen Aufruf (vtable).Einschränkungen:
@objc markiert sind.@objc oder die durch Vererbung von NSObject markiert sind.Beispiel:
import Foundation class Person: NSObject { @objc dynamic func sayHello() -> String { return "Hallo!" } } let person = Person() // Dynamischer Aufruf über Selector (perform:): if person.responds(to: #selector(Person.sayHello)) { if let result = person.perform(#selector(Person.sayHello))?.takeUnretainedValue() as? String { print(result) // "Hallo!" } }
Ähnlich kann man Funktionen über AnyObject mit optional chaining aufrufen:
@objc protocol Greeter { @objc optional func greet() } class Robot: NSObject, Greeter { func greet() { print("Beep-beep!") } } let anyGreeter: AnyObject = Robot() anyGreeter.greet?() // "Beep-beep!"
Kann ein struct den dynamischen Methodenaufruf über @objc, dynamic oder AnyObject unterstützen?
Antwort: Nein, nur Klassen, die von NSObject abgeleitet sind, oder Klassen/Protokolle mit dem Modifikator @objc können solche Mechanismen unterstützen. Structs und enums sind nicht mit der Objective-C-Laufzeit kompatibel, daher können sie keine dynamischen Mitglieder sein.
Geschichte
Im Projekt wurde ein @objc-Protokoll für den Delegate verwendet. Einer der Entwickler entfernte die Vererbung der Delegatenklasse von NSObject, wodurch die optionalen Methoden nicht mehr über optional chaining aufgerufen werden konnten. Die Architektur funktionierte nicht mehr, Tests fielen ständig durch.
Geschichte
In dem Versuch, KVO (key-value observing) auf einer Eigenschaft eines Structs zu verwenden, markierte der Entwickler diese als dynamic. Der Code wurde kompiliert, funktionierte aber nicht wie erwartet, da Structs die Objective-C-Laufzeit nicht unterstützen. Die dynamische Reaktion auf Änderungen funktionierte nicht, was zu übergangenen UI-Updates führte.
Geschichte
In der Erweiterung der Klasse wurde vergessen, den Modifikator @objc für die Funktion hinzuzufügen, die über perform(_: ) aufgerufen wurde. Infolgedessen traten in der Produktion Abstürze auf, wenn versucht wurde, einen unverbundenen Selector aufzurufen. Der Grund wurde lange gesucht, bis das Fehlen des Exports der Funktion in Objective-C bemerkt wurde.