Почему Swift 3 @objc inference уходит в прошлое?
Если вы когда-нибудь обновляли проект с Swift 3 на Swift 4, то наверняка сталкивались с пугающим предупреждением компилятора:
The use of Swift 3 @objc inference in Swift 4 mode is deprecated.
Please address this deprecated @objc inference warning,
or disable the warning by setting the "Swift 3 @objc Inference"
build setting to "Default" for the "Swift 4" target.
Это сообщение — не просто формальность. За ним стоит важное изменение философии языка. В Swift 3 компилятор сам решал, какие методы и свойства нужно сделать видимыми для Objective-C. Это было удобно, но создавало скрытые проблемы: бинарный размер рос, а производительность падала. Swift 4 решил покончить с этим «гаданием» и передал контроль разработчику.
В этой статье мы разберём, что такое @objc inference, почему его решили deprecated, как исправить предупреждение и какие лучшие практики использовать в современных проектах. Поехали!
Что такое @objc inference и как он работал в Swift 3?
В Swift 3 существовало неявное приведение к @objc. Это означало, что если класс наследовался от NSObject или использовал @objc-протокол, компилятор автоматически делал все его методы видимыми для Objective-C. Механизм назывался @objc inference (неявный вывод).
Пример из Swift 3:
class MyViewController: UIViewController {
func buttonTapped() {
print("Button tapped")
}
}
В Swift 3 метод buttonTapped() автоматически становился @objc. Вы могли передать его в #selector или использовать как target для UIControl. Удобно? Да. Но цена — неоправданное раздувание бинарника и потеря оптимизаций.
Почему Apple решила отказаться от автоматического вывода?
Причин несколько, и все они — про эффективность и контроль:
- Размер приложения. Каждый метод, помеченный как @objc, генерирует дополнительную обёртку для Objective-C runtime. В больших проектах это могло добавить мегабайты лишнего кода.
- Производительность. Swift — язык со статической типизацией и мощной оптимизацией. Как только метод становится @objc, компилятор теряет возможность применять ряд оптимизаций (например, инлайнинг).
- Явность. Swift 4 взял курс на явность. Разработчик должен сам решать, что должно быть доступно из Objective-C. Это упрощает понимание кода и уменьшает неожиданные ошибки.
- Будущее Swift. Apple постепенно уходит от зависимости от Objective-C. Чем меньше неявных мостов, тем быстрее Swift сможет стать полностью самостоятельным языком.
Как выглядит предупреждение на практике?
Допустим, у вас есть проект, написанный на Swift 3. Вы переключаете настройку "Swift Language Version" на Swift 4.0 и получаете десятки (а то и сотни) таких предупреждений:
class LegacyViewController: UIViewController {
// Предупреждение: The use of Swift 3 @objc inference in Swift 4 mode is deprecated
func legacyAction() {
// ...
}
@objc func explicitAction() {
// Этот метод уже помечен @objc — всё хорошо
}
}
Компилятор ругается на legacyAction(), так как он не помечен @objc, но раньше компилятор делал это автоматически. Теперь нужно решение.
Три способа исправить предупреждение
У вас есть три варианта действий. Выберите тот, который подходит вашему проекту.
1. Добавить @objc вручную к каждому методу
Самый правильный и рекомендуемый способ. Вы явно указываете, какие методы должны быть доступны из Objective-C:
class MyViewController: UIViewController {
@objc func buttonTapped() {
print("Button tapped")
}
@objc func handleGesture() {
print("Gesture handled")
}
}
Плюсы: полный контроль, никаких лишних обёрток, чистый код.
Минусы: нужно перебрать весь проект и добавить @objc везде, где это необходимо.
2. Унаследоваться от NSObject и добавить @objcMembers к классу
Если вам нужно, чтобы все методы класса были видны из Objective-C, можно использовать атрибут @objcMembers:
@objcMembers class MyViewController: UIViewController {
func buttonTapped() {
print("Button tapped")
}
func handleGesture() {
print("Gesture handled")
}
}
Этот подход делает все методы и свойства класса @objc (кроме статических и приватных). Он удобен для классов, которые активно взаимодействуют с Objective-C (например, UIViewController).
Плюсы: быстрое решение для целого класса, не нужно ставить @objc перед каждым методом.
Минусы: по-прежнему генерируются лишние обёртки для методов, которые не нужны в Objective-C. Не рекомендуется для больших классов с множеством методов.
3. Отключить предупреждение на уровне сборки
Если вы хотите отложить миграцию на потом, можно просто отключить предупреждение. Для этого в настройках проекта (Build Settings) найдите параметр "Swift 3 @objc Inference" и установите его в "Default" (или "On").
Плюсы: быстро, ничего не нужно менять в коде.
Минусы: вы просто игнорируете проблему. Код продолжает работать, но вы теряете преимущества Swift 4. Это временное решение, не для продакшена.
Сравнение подходов: что выбрать?
| Подход | Когда использовать | Недостатки |
|---|---|---|
| Явный @objc | Для небольших проектов, где нужно точечно контролировать видимость | Много ручной работы |
| @objcMembers | Для классов, где большинство методов используются из Objective-C | Лишние обёртки для всех методов |
| Отключение предупреждения | Только для быстрого перехода на Swift 4 с последующим рефакторингом | Не решает
БлогиРекомендаций по книгамЧитайте также |