ProgrammazioneSviluppatore iOS

Как работает динамический вызов методов в Swift? Какие механизмы (например, @objc, dynamic, AnyObject) позволяют вызывать методы по имени во время выполнения, и при каких условиях это возможно? Приведите пример использования.

Supera i colloqui con l'assistente IA Hintsage

Ответ

Динамический вызов методов — это возможность вызывать методы по имени во время выполнения (runtime), а не во время компиляции. В Swift это возможно через взаимодействие с Objective-C runtime благодаря модификаторам @objc и dynamic, либо при использовании протокола AnyObject.

Механизмы:

  • @objc — помечает метод/свойство/класс для экспорта в Objective-C runtime.
  • dynamic — требует выполнения через Objective-C message dispatch, а не через статический вызов (таблицу vtable).
  • Протокол AnyObject позволяет объявить методы как optional и разрешает их вызывать через optional chaining.

Ограничения:

  • Работает только с классами, наследующимися от NSObject, или с протоколами, помеченными как @objc.
  • Касается только методов/свойств с атрибутом @objc или помеченных через наследование от NSObject.

Пример:

import Foundation class Person: NSObject { @objc dynamic func sayHello() -> String { return "Hello!" } } let person = Person() // Динамический вызов через Selector (perform:): if person.responds(to: #selector(Person.sayHello)) { if let result = person.perform(#selector(Person.sayHello))?.takeUnretainedValue() as? String { print(result) // "Hello!" } }

Аналогично, можно вызывать функции через AnyObject с 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!"

Вопрос с подвохом

Может ли struct поддерживать динамический вызов методов через @objc, dynamic или AnyObject?

Ответ: Нет, только классы, унаследованные от NSObject, или классы/протоколы с модификатором @objc могут поддерживать такие механизмы. Struct и enum не совместимы с Objective-C runtime, поэтому они не могут быть динамическими участниками.


Примеры реальных ошибок из-за незнания тонкостей темы


История

В проекте использовали @objc protocol для делегата. Один из разработчиков снял наследование класса делегата от NSObject, вследствие чего optional-методы перестали быть доступными для вызова через optional chaining. Архитектура перестала работать, тесты стали постоянно падать.


История

В попытке использовать KVO (key-value observing) на свойстве структуры разработчик пометил его как dynamic. Код скомпилировался, но не работал как ожидалось, т.к. struct не поддерживает Objective-C runtime. Динамическая реакция на изменения не работала, что привело к пропущенным обновлениям UI.


История

В экстеншене класса забыли добавить модификатор @objc к функции, которую вызывали через perform(_:). В результате на проде возникали крэши при попытке вызвать несвязанный селектор. Причину долго искали, пока не заметили отсутствие экспорта функции в Objective-C.