Swift 3 @objc inference deprecated: что делать в Swift 4

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Почему 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 с последующим рефакторингом Не решает

Блоги

Рекомендаций по книгам