Как я могу справиться с дефолтом вывода @objc с помощью #selector() в Swift 4?

Я пытаюсь преобразовать исходный код проекта из Swift 3 в Swift 4. Одно предупреждение Xcode дает мне о моих селекторах.

Например, я добавляю цель к кнопке с помощью обычного селектора, например:

button.addTarget(self, action: #selector(self.myAction), for: .touchUpInside)

Это предупреждение, которое он показывает:

Аргумент '#selector' относится к методу экземпляра 'myAction()' в 'ViewController', который зависит от вывода атрибута '@objc', устаревшего в Swift 4

Добавьте '@objc', чтобы открыть этот метод экземпляра Objective-C

Теперь, нажав Fix на сообщение об ошибке, это применит к моей функции:

// before
func myAction() { /* ... */ }

// after
@objc func myAction() { /* ... */ }

Я действительно не хочу переименовывать все мои функции, чтобы включить знак @objc и я предполагаю, что это не обязательно.

Как переписать селектор для устранения устаревания?


Связанный вопрос:

Ответ 1

Исправить - это правильно - ничего о селекторе, который вы можете изменить, чтобы сделать метод, на который он ссылается, подвергается Objective-C.

Вся причина этого предупреждения в первую очередь - результат SE-0160. До Swift 4, internal или выше Objective-C совместимые члены классов наследования NSObject были определены как @objc и поэтому подвергались воздействию Objective-C, поэтому позволяли им вызываться с использованием селекторов (как Obj-C время выполнения требуется для поиска реализации метода для данного селектора).

Однако в Swift 4 это уже не так. В настоящее время допускаются только очень конкретные объявления: @objc, например, переопределения методов @objc, реализации требований протокола @objc и деклараций с атрибутами, которые подразумевают @objc, например @IBOutlet.

Мотивация этого, как указано в в вышеупомянутом связанном предложении, во-первых, заключается в предотвращении перегрузки методов в NSObject наследовании классов от столкновений с друг друга из-за наличия идентичных селекторов. Во-вторых, это помогает уменьшить размер двоичного файла, не создавая трюков для членов, которые не должны подвергаться Obj-C, и, в-третьих, улучшает скорость динамической компоновки.

Если вы хотите выставить элемент в Obj-C, вам нужно пометить его как @objc, например:

class ViewController: UIViewController {

    @IBOutlet weak var button: UIButton!

    override func viewDidLoad() {
        super.viewDidLoad()
        button.addTarget(self, action: #selector(foo), for: .touchUpInside)
    }

    @objc func foo() {
       // ... 
    }
}

(мигратор должен сделать это автоматически для вас с помощью селекторов при запуске с опцией "минимизировать вывод" )

Чтобы выставить группу членов в Obj-C, вы можете использовать @objc extension:

@objc extension ViewController {

    // both exposed to Obj-C
    func foo() {}
    func bar() {}
}

Это выведет все члены, определенные в нем, в Obj-C и даст ошибку для любых членов, которые не могут быть подвергнуты Obj-C (если явно не указано как @nonobjc).

Если у вас есть класс, в котором вам нужно, чтобы все члены, совместимые с Obj-C, подвергались воздействию Obj-C, вы можете пометить класс как @objcMembers:

@objcMembers
class ViewController: UIViewController {
   // ...
}

Теперь все члены, которые могут быть определены как @objc, будут. Тем не менее, я бы не советовал делать это, если вам действительно не нужны все участники, подвергшиеся воздействию Obj-C, учитывая вышеупомянутые недостатки, связанные с тем, что члены были необоснованными.

Ответ 2

Официальная документация Apple. вам нужно использовать @objc для вызова вашего метода выбора.

В Objective-C селектор - это тип, который ссылается на имя метода Objective-C. В Swift селекторы Objective-C представлены структурой Selector и могут быть построены с использованием выражения #selector. Чтобы создать селектор для метода, который можно вызвать из Objective-C, передайте имя метода, например #selector(MyViewController.tappedButton(sender:)). Чтобы построить селектор для метода getter или setter объекта Objective-C, передайте имя свойства, префиксное меткой getter: или setter: label, например #selector(getter: MyViewController.myButton).

Ответ 3

На мой взгляд, Swift 4.2, все, что вам нужно сделать, это назначить @IBAction для вашего метода, и вы можете избежать этой глупой аннотации @objc

"""

let tap  =  UITapGestureRecognizer(target: self, action: #selector(self.cancel))


@IBAction func cancel()
{
    self.dismiss(animated: true, completion: nil)
}

Ответ 4

Как уже упоминалось в других ответах, нет способа избежать аннотации @objc для селекторов.

Но предупреждение, упомянутое в OP, можно заставить замолчать, сделав следующие шаги:

  1. Перейти в настройки сборки
  2. Поиск по ключевому слову @objc
  3. Установите значение интерфейса Swift 3 @objc на Off

ниже скриншот, который иллюстрирует вышеупомянутые шаги:

Silencing the warning "Swift 3 @objc interface"

Надеюсь это поможет

Ответ 5

Если вам нужны члены target c в вашем контроллере представления, просто добавьте @objcMembers в верхней части контроллера представления. И вы можете избежать этого, добавив IBAction в ваш код.

'@IBAction func buttonAction() {

}"

Обязательно подключите эту розетку к раскадровке.