Як я можу впоратися з припиненням виводу @objc за допомогою #selector () у Swift 4?


131

Я намагаюся перетворити вихідний код мого проекту з 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позначку, і я припускаю, що це не потрібно.

Як переписати перемикач, щоб вирішити питання про депресію?


Питання, пов'язані з цим:


3
Ні, позначаючи їх такими @objc , що зараз необхідні для того, щоб виставити їх Obj-C, а тому використовувати разом із селекторами.
Гаміш

3
Тож застаріла частина виконує функції публічного доступу як @objc? Це трохи дратує, але я, як правило, роблю ці функції приватними, вимагаючи від мене позначити це як @objcзавгодно.
Коннор

2
Див. SE-0160 для отримання додаткової інформації про зміни. Іншою альтернативою є маркування даного класу як @objcMembersдля того, щоб виставити всі Obj-C сумісні члени Obj-C, але я б не радив цього, якщо вам насправді не потрібен весь ваш клас.
Гаміш

3
@LinusGeffarth Як сказано в пропозиції, це, ймовірно, зайве збільшення розміру вашої бінарної, і динамічне посилання потребує більше часу. Я дійсно не думаю, що це занадто багато клопоту з додатковою чіткістю, яку ви конкретно маєте на увазі для конкретної речі, що використовується від Obj-C.
Гаміш

1
Спробував, не працює.
LinusGeffarth

Відповіді:


156

Виправити це правильно - немає нічого про селектор, який ви можете змінити, щоб зробити метод, на який він посилається, піддається об’єкту-Objective-C.

Вся причина цього попередження в першу чергу є результатом SE-0160 . До Swift 4 internalабо вище об'єктів, що сумісні з Objective-C, NSObjectкласів успадковування було зроблено висновок, @objcа тому вони піддаються об'єкту-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
Чому Xcode не робить цього автоматично під час перетворення коду в останній синтаксис?
LinusGeffarth

1
Цікаво, чи @IBActionтеж автоматично @objc? досі це було не так, але це було б логічно. EDIT: nvm, у пропозиції чітко зазначено, що @IBActionцього методу достатньо @objc.
Султан

8
Я нічого з цього не розумію. У мене немає об'єктивного-C-коду, що досі. Чи немає чистого способу додавання цілей до кнопки? Або використовувати селектори? Я не хочу, щоб мій код був прошитий цим атрибутом. Це тому, що UIButton є похідним від якоїсь NSObject / Obj-c-речі?
Сті

11
@Sti Отже, щоб безпосередньо відповісти " Чи немає чистого способу додавання цілей до кнопки? Або використовувати селектори? " - ні, немає "чистого" способу використання селекторів для відправки на методи. Вони покладаються на час виконання Obj-C для того, щоб знайти реалізацію методу для виклику конкретного селектора - час виконання Swift не має такої можливості.
Гаміш

12
Селектори людини - безлад. Схоже, що будь-яке інше швидке оновлення вони з цим возиться. Чому ми не можемо просто мати чистий швидкий селектор автозаповнення методів. Робить код таким некрасивим.
Джон Різельвато

16

Як офіційна документація Apple . вам потрібно використовувати @objc для виклику вашого методу вибору.

У Objective-C селектор - це тип, який посилається на назву методу Objective-C. У Swift селектори Objective-C представлені Selectorструктурою і можуть бути побудовані за допомогою #selector виразу. Щоб створити селектор для методу, який можна викликати з Objective-C, передайте ім'я методу, наприклад #selector(MyViewController.tappedButton(sender:)). Щоб сконструювати селектор для методу Getter або Cetter властивості властивості, передайте ім'я властивості з префіксом getter:або setter:міткою, наприклад #selector(getter: MyViewController.myButton).


Все, що я розумію з цього, це те, що в деяких випадках мені потрібно додати @objc до початку функції, незалежно від того, чи збираюся я коли-небудь називати це з Objective-C. Я просто навчаюсь Свіфта, використовуючи Obj-C протягом багатьох років
SundialSoft

10

Я думаю, Swift 4.2, все, що вам потрібно зробити, - це призначити @IBAction своєму методу, і ви можете уникнути цієї нерозумної анотації @objc

`` `

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


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

1
Це також скаже програмісту інтерфейсу, що цю функцію можна підключити, що може бути не тим, що потрібно. Це також робить те саме, що і @objc.
Джош Парадроїд

2

Як уже згадувалося в інших відповідях, немає можливості уникнути @objcанотації для селекторів.

Але попередження, згадане в ОП, можна приглушити, зробивши наступні кроки:

  1. Перейдіть до Налаштування збірки
  2. Пошук за ключовим словом @objc
  3. Встановіть значення інтерфейсу Swift 3 @objc наOff

нижче - скріншот, який ілюструє вищезазначені кроки:

Безмовне застереження "Інтерфейс Swift 3 @objc"

Сподіваюся, це допомагає


Як це впливає на ваш проект в цілому?
Зонілі Джейм

1
Як щодо розміру * .ipa або * .xarchive та часу побудови?
Зонілі Джейм

@ZonilyJame Я не помітив час збирання, але на розмір IPA не вплинуло
S1LENT WARRIOR

Це не рекомендоване значення, яке ви можете використовувати, якщо ви використовуєте швидку версію 4.0+, відповідно до рекомендацій Apple. Перевірте цю допомогу.apple.com
xcode/

1
Цю відповідь можна оновити, встановивши значення X за замовчуванням у Xcode 11. Важливо встановити значення як для проекту, так і для цілей, щоб повністю зняти попередження.
Tommie C.

2

Якщо вам потрібні об'єктивні члени c у контролері перегляду, просто додайте @objcMembers у верхній частині контролера перегляду. І уникнути цього можна, додавши у свій код IBAction.

@IBAction func buttonAction() {

}

Обов’язково підключіть цю розетку до розкадрівки.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.