Устарело ли использование @objc inference в Swift 4 mode? Как исправить?
Что такое @objc inference и почему оно устарело?
В Swift 3 компилятор автоматически добавлял атрибут @objc к любому члену класса, унаследованного от NSObject, если это было необходимо для совместимости с Objective-C. Это называлось @objc inference. Проблема в том, что такое неявное поведение увеличивало размер бинарного файла и замедляло компиляцию, так как компилятору приходилось анализировать все методы и свойства на предмет возможного использования из Objective-C.
В Swift 4 это поведение было признано устаревшим. Теперь вы должны явно указывать, какие именно элементы должны быть доступны из Objective-C, с помощью атрибута @objc. Это дает больше контроля и повышает производительность.
Как исправить проблему?
Есть несколько способов решения. Рассмотрим их на вашем примере:
class MyClass: NSObject {
var name: String = ""
func doSomething() {
print("Doing something")
}
}
Способ 1: Добавить @objc к каждому члену
Самый простой способ — явно пометить каждый метод или свойство, которое должно быть доступно из Objective-C:
class MyClass: NSObject {
@objc var name: String = ""
@objc func doSomething() {
print("Doing something")
}
}
Способ 2: Добавить @objcMembers ко всему классу
Если большая часть методов и свойств класса должна быть доступна из Objective-C, можно пометить весь класс атрибутом @objcMembers. Это эквивалентно добавлению @objc ко всем членам класса, кроме тех, что явно помечены @nonobjc:
@objcMembers class MyClass: NSObject {
var name: String = "" // автоматически @objc
func doSomething() { // автоматически @objc
print("Doing something")
}
}
Способ 3: Использовать расширение с @objc
Если вы хотите отделить Objective-C совместимый код от чистого Swift, можно вынести методы в расширение:
class MyClass: NSObject {
var name: String = ""
}
@objc extension MyClass {
func doSomething() {
print("Doing something")
}
}
Дополнительные рекомендации
- Не злоупотребляйте @objcMembers. Используйте его только если класс действительно активно используется из Objective-C. Иначе вы получите тот же недостаток, что и в Swift 3 — лишний код.
- Проверьте настройки проекта. В Xcode 9+ в настройках проекта (Build Settings) есть параметр
Swift 3 @objc Inference. По умолчанию он стоит вDefault(для Swift 4 — это "отключено"). Вы можете временно установить его вOn, чтобы подавить предупреждения, но это не рекомендуется — лучше сразу исправить код. - Используйте #selector. Если вы используете селекторы (например, для target-action), обязательно добавьте
@objcк методу, иначе получите ошибку компиляции.
В итоге, вам не нужно переписывать весь проект. Достаточно пройтись по предупреждениям Xcode и добавить @objc в нужные места. Это займет немного времени, но сделает код более явным и эффективным.
Альтернативный подход: Использование протоколов
Если ваш класс должен быть совместим с Objective-C только для определенного набора методов, вы можете определить протокол, унаследованный от NSObjectProtocol, и пометить его @objc. Затем реализовать этот протокол в классе. Это дает более четкое разделение ответственности:
@objc protocol MyObjcCompatible {
func doSomething()
}
class MyClass: NSObject, MyObjcCompatible {
var name: String = ""
@objc func doSomething() {
print("Doing something")
}
}
Обратите внимание: даже при использовании протокола, реализующие методы все равно нужно помечать @objc (если они не входят в @objcMembers класс).
Еще один совет: Автоматическая миграция
Xcode 9+ предлагает автоматическое исправление предупреждений. Кликните на желтый треугольник слева от строки с предупреждением, и выберите "Add @objc" или "Add @objcMembers". Это сработает для простых случаев, но для сложных иерархий лучше проверить вручную.
Также рекомендую использовать #warning или FIXME для отслеживания мест, где вы временно оставили @objc, чтобы позже пересмотреть необходимость совместимости с Objective-C.
Ответов пока нет.
Войдите чтобы оставить ответ