ProgrammationDéveloppeur iOS

Comment fonctionne l'appel dynamique de méthodes en Swift ? Quels mécanismes (comme @objc, dynamic, AnyObject) permettent d'appeler des méthodes par nom à l'exécution, et dans quelles conditions cela est-il possible ? Donnez un exemple d'utilisation.

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse

L'appel dynamique de méthodes est la possibilité d'appeler des méthodes par nom à l'exécution (runtime), et non au moment de la compilation. En Swift, cela est possible grâce à l'interaction avec l'Objective-C runtime grâce aux modificateurs @objc et dynamic, ou à l'utilisation du protocole AnyObject.

Mécanismes :

  • @objc — marque une méthode/propriété/classe pour l'exportation vers l'Objective-C runtime.
  • dynamic — nécessite une exécution via le dispatch de message Objective-C, et non via un appel statique (tableau vtable).
  • Le protocole AnyObject permet de déclarer les méthodes comme optionnelles et permet de les appeler via l'optional chaining.

Limitations :

  • Ne fonctionne qu'avec des classes qui héritent de NSObject, ou des protocoles marqués comme @objc.
  • Concerne uniquement les méthodes/propriétés avec l'attribut @objc ou marquées par héritage de NSObject.

Exemple :

import Foundation class Person: NSObject { @objc dynamic func sayHello() -> String { return "Hello!" } } let person = Person() // Appel dynamique via Selector (perform :) : if person.responds(to: #selector(Person.sayHello)) { if let result = person.perform(#selector(Person.sayHello))?.takeUnretainedValue() as? String { print(result) // "Hello!" } }

De la même manière, on peut appeler des fonctions via AnyObject avec 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!"

Question piège

Un struct peut-il supporter l'appel dynamique de méthodes via @objc, dynamic ou AnyObject ?

Réponse : Non, seules les classes héritées de NSObject, ou les classes/protocoles avec le modificateur @objc peuvent supporter de tels mécanismes. Les structs et enums ne sont pas compatibles avec l'Objective-C runtime, donc elles ne peuvent pas être des participants dynamiques.


Exemples d'erreurs réelles dues à l'ignorance des subtilités du sujet


Histoire

Dans un projet, un protocole @objc a été utilisé pour le délégué. L'un des développeurs a retiré l'héritage de la classe déléguée de NSObject, entraînant ainsi l'inaccessibilité des méthodes optionnelles pour l'appel via optional chaining. L'architecture a cessé de fonctionner, les tests ont commencé à échouer en continu.


Histoire

Dans une tentative d'utiliser KVO (key-value observing) sur une propriété de structure, le développeur l'a marquée comme dynamic. Le code a été compilé, mais n'a pas fonctionné comme prévu, car le struct ne supporte pas l'Objective-C runtime. La réaction dynamique aux changements ne fonctionnait pas, ce qui a entraîné des mises à jour d'UI manquées.


Histoire

Dans une extension de classe, on a oublié d'ajouter le modificateur @objc à la fonction appelée via perform(_:). En conséquence, des crashs se produisaient en production lors de tentatives d'appel d'un sélecteur non lié. La cause a été recherchée longtemps, jusqu'à ce que l'absence d'exportation de la fonction vers Objective-C soit remarquée.