Почему в Swift 4 устарело неявное @objc и как это исправить?
Почему это произошло?
В Swift 3 компилятор автоматически добавлял атрибут @objc к методам, которые могли быть вызваны из Objective-C (например, через #selector или target-action). Это называлось «неявный вывод @objc». В Swift 4 такое поведение стало устаревшим, потому что:
- Оно создавало много скрытого Objective-C кода, который увеличивал размер бинарника.
- Затрудняло понимание, какие методы действительно доступны из Objective-C.
- Мешало оптимизациям компилятора (например, инлайнингу).
В Swift 4 вы должны явно указывать @objc для методов, которые используются с #selector или вызываются из Objective-C. Исключение — методы, переопределяющие @objc методы из суперкласса (например, viewDidLoad), они наследуют атрибут автоматически.
Как исправить предупреждения?
Самый простой способ — добавить @objc перед методом. Исправленный пример из вопроса:
class MyViewController: UIViewController {
@IBOutlet weak var myButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
myButton.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
}
@objc func buttonTapped() {
print("Button tapped")
}
}
Если у вас много методов, которые нужно сделать доступными для Objective-C, вы можете использовать @objcMembers перед классом. Тогда все методы класса (кроме private) автоматически получат @objc:
@objcMembers
class MyViewController: UIViewController {
// Все методы здесь будут @objc
func buttonTapped() { }
func anotherAction() { }
}
Но будьте осторожны: @objcMembers включает все методы, что может увеличить размер бинарника. Лучше использовать его только для небольших классов или на время миграции.
Что делать с большим проектом?
Для массовой миграции:
- Используйте фикс Xcode: В меню Edit → Convert → To Current Swift Syntax… Xcode сам добавит
@objcкуда нужно. - Настройте уровень вывода: В настройках проекта (Build Settings) найдите
Swift 3 @objc Inferenceи установитеDefault(чтобы временно подавить предупреждения) илиOn(для полной обратной совместимости, но это не рекомендуется). - Постепенно исправляйте: Установите
Swift 3 @objc InferenceвOnдля всей сборки, затем по одному файлу переключайте на Swift 4, исправляя предупреждения.
Игнорировать предупреждения не стоит — в будущих версиях Swift (5 и выше) это станет ошибкой. Лучше сразу привыкнуть к явному указанию @objc.
Альтернативный подход: Использование расширений и протоколов
Если вы не хотите добавлять @objc к каждому методу в классе, можно вынести Objective-C совместимые методы в отдельное расширение с атрибутом @objc:
class MyViewController: UIViewController {
@IBOutlet weak var myButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
myButton.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
}
}
@objc extension MyViewController {
func buttonTapped() {
print("Button tapped")
}
}
Этот подход помогает лучше структурировать код: все методы, которые должны быть видны из Objective-C, собраны в одном месте. Плюс вы не рискуете случайно сделать @objc лишние методы, как в случае с @objcMembers.
Также можно использовать протоколы с атрибутом @objc:
@objc protocol ButtonActions {
func buttonTapped()
}
class MyViewController: UIViewController, ButtonActions {
// реализация
}
Это полезно, если вы работаете с делегатами или кастомными target-action паттернами. Но для простых случаев достаточно добавить @objc к конкретному методу.
Ответов пока нет.
Войдите чтобы оставить ответ