Как справиться с устареванием @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, и я предполагаю, что в этом нет необходимости.

Как мне переписать селектор, чтобы справиться с устареванием?


Смежный вопрос:

113 голосов | спросил LinusGeffarth 6 J0000006Europe/Moscow 2017, 15:44:48

1 ответ


0

Исправление - это правильно - в селекторе нет ничего, что можно изменить, чтобы сделать метод, на который он ссылается, доступным для 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, учитывая вышеупомянутые недостатки излишнего раскрытия членов.

ответил Hamish 6 J0000006Europe/Moscow 2017, 16:29:56

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132