Dynamiczne wywołanie metod to możliwość wywoływania metod po nazwie w czasie wykonania (runtime), a nie w czasie kompilacji. W Swift jest to możliwe dzięki interakcji z Objective-C runtime dzięki modyfikatorom @objc i dynamic, lub przy użyciu protokołu AnyObject.
Mechanizmy:
@objc — oznacza metodę/właściwość/klasę do eksportu do Objective-C runtime.dynamic — wymaga wykonania za pomocą Objective-C message dispatch, a nie przez statyczne wywołanie (tabela vtable).Ograniczenia:
@objc.@objc lub oznaczonych przez dziedziczenie z NSObject.Przykład:
import Foundation class Person: NSObject { @objc dynamic func sayHello() -> String { return "Hello!" } } let person = Person() // Dynamiczne wywołanie przez Selector (perform:): if person.responds(to: #selector(Person.sayHello)) { if let result = person.perform(#selector(Person.sayHello))?.takeUnretainedValue() as? String { print(result) // "Hello!" } }
Podobnie, można wywoływać funkcje przez AnyObject z 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!"
Czy struct może wspierać dynamiczne wywołanie metod przez @objc, dynamic lub AnyObject?
Odpowiedź: Nie, tylko klasy dziedziczące z NSObject, lub klasy/protokóły z modyfikatorem @objc mogą wspierać takie mechanizmy. Struct i enum nie są zgodne z Objective-C runtime, więc nie mogą być dynamicznymi uczestnikami.
Historia
W projekcie używano protokołu @objc dla delegata. Jeden z programistów usunął dziedziczenie klasy delegata z NSObject, w wyniku czego opcjonalne metody przestały być dostępne do wywołania przez optional chaining. Architektura przestała działać, testy zaczęły ciągle padać.
Historia
W próbie użycia KVO (key-value observing) na właściwości struktury, programista oznaczył ją jako dynamic. Kod skompilował się, ale nie działał zgodnie z oczekiwaniami, ponieważ struct nie wspiera Objective-C runtime. Dynamiczna reakcja na zmiany nie działała, co prowadziło do pominięcia aktualizacji UI.
Historia
W rozszerzeniu klasy zapomniano dodać modyfikator @objc do funkcji, którą wywoływano przez perform(_:). W rezultacie na produkcji występowały awarie podczas próby wywołania niepowiązanego selektora. Powód długo szukano, aż zauważono brak eksportu funkcji do Objective-C.