La llamada dinámica de métodos es la capacidad de invocar métodos por nombre en tiempo de ejecución, en lugar de en tiempo de compilación. En Swift, esto es posible a través de la interacción con el runtime de Objective-C gracias a los modificadores @objc y dynamic, o al usar el protocolo AnyObject.
Mecanismos:
@objc — marca el método/propiedad/clase para exportación al runtime de Objective-C.dynamic — requiere la ejecución a través del despacho de mensajes de Objective-C, y no a través de la llamada estática (tabla vtable).Limitaciones:
@objc.@objc o marcados mediante herencia de NSObject.Ejemplo:
import Foundation class Person: NSObject { @objc dynamic func sayHello() -> String { return "¡Hola!" } } let person = Person() // Llamada dinámica a través de Selector (perform:): if person.responds(to: #selector(Person.sayHello)) { if let result = person.perform(#selector(Person.sayHello))?.takeUnretainedValue() as? String { print(result) // "¡Hola!" } }
De manera similar, se pueden llamar funciones a través de AnyObject con 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!"
¿Puede una struct soportar la llamada dinámica de métodos a través de @objc, dynamic o AnyObject?
Respuesta: No, solo las clases heredadas de NSObject, o las clases/protocolos con el modificador @objc pueden soportar tales mecanismos. Las struct y enum no son compatibles con el runtime de Objective-C, por lo que no pueden ser participantes dinámicos.
Historia
En el proyecto se utilizó un protocolo @objc para el delegado. Uno de los desarrolladores eliminó la herencia de la clase delegada de NSObject, lo que provocó que los métodos opcionales dejaran de estar disponibles para su llamada a través de optional chaining. La arquitectura dejó de funcionar y las pruebas comenzaron a fallar.
Historia
En un intento de utilizar KVO (observación de clave-valor) en la propiedad de una struct, el desarrollador la marcó como dynamic. El código se compiló, pero no funcionó como se esperaba, ya que la struct no soporta el runtime de Objective-C. La reacción dinámica a los cambios no funcionó, lo que llevó a actualizaciones de UI perdidas.
Historia
En la extensión de una clase, olvidaron agregar el modificador @objc a la función que se llamaba a través de perform(_:). Como resultado, ocurrían fallas en producción al intentar llamar a un selector no relacionado. La causa se buscó durante mucho tiempo hasta que se notó la falta de exportación de la función a Objective-C.