Хоча я думаю, що можу відповісти на ваше запитання, це вам не сподобається відповідь.
TL; DR: @objc
функції можуть не бути в розширеннях протоколів. Ви можете створити базовий клас натомість, хоча це не ідеальне рішення.
Розширення протоколу та ціль-C
По-перше, це питання / відповідь ( Чи може метод Swift визначений у розширеннях на протоколи, доступні в Objective-c ), начебто підказує, що через те, як розширення протоколу розсилаються під кришкою, методи, оголошені в розширеннях протоколу, не видно objc_msgSend()
функції, і тому не видно коду Objective-C. Оскільки метод, який ви намагаєтеся визначити у своєму розширенні, повинен бути видимим для Objective-C (щоб його UIKit
можна було використовувати), він кричить на вас за невключення @objc
, але як тільки ви включите його, він кричить на вас, тому що @objc
це не дозволено в розширення протоколу. Це, мабуть, тому, що розширення протоколів наразі не можуть бути видимими для Objective-C.
Ми також можемо побачити, що повідомлення про помилку, коли ми додаємо, @objc
констатує "@objc можна використовувати лише з членами класів, протоколами @objc та конкретними розширеннями класів." Це не клас; розширення на протокол @objc - це не те саме, що в самому визначенні протоколу (тобто у вимогах), а слово "конкретний" припускає, що розширення протоколу не вважається конкретним розширенням класу.
Обхід
На жаль, це в значній мірі повністю заважає використовувати розширення протоколів, коли реалізації за замовчуванням повинні бути видні рамкам Objective-C. Спочатку я подумав, що, можливо, @objc
це не було дозволено у вашому розширенні протоколу, оскільки Swift Compiler не міг гарантувати, що відповідні типи будуть класами (навіть якщо ви конкретно вказали UIViewController
). Тому я ставлю class
вимогу P1
. Це не вийшло.
Мабуть, єдине вирішення - просто використовувати базовий клас замість протоколу, але це, очевидно, не зовсім ідеально, оскільки клас може мати лише один базовий клас, але відповідати декільком протоколам.
Якщо ви вирішили пройти цей маршрут, будь ласка, врахуйте це питання ( Swift 3 ObjC Необов’язковий протокол, метод не викликається в підкласі ). Очевидно, що ще одна актуальна проблема у Swift 3 полягає в тому, що підкласи не успадковують автоматично необов'язкові реалізації протокольних вимог їхнього суперкласу. Відповідь на ці запитання використовує спеціальну адаптацію, @objc
щоб обійти її.
Повідомлення про випуск
Я думаю, що це вже обговорюється серед тих, хто працює над проектами з відкритим кодом Swift, але ви можете бути впевнені, що вони знають це, використовуючи Apple Bug Reporter , який, можливо, врешті-решт пробиться до основної команди Swift , або репортера помилок Swift . Проте, будь-яка з цих помилок може визнати вашу помилку занадто широкою або вже відомою. Команда Swift також може розглянути те, що ви шукаєте, як нову мовну функцію, і в цьому випадку спочатку слід ознайомитися зі списками розсилки .
Оновлення
У грудні 2016 року про це питання було повідомлено спільноті Swift. Випуск все ще позначений як відкритий із середнім пріоритетом, але додано наступний коментар:
Це призначено. Немає можливості додати реалізацію методу до кожного прийнятого, оскільки розширення може бути додане після відповідності протоколу. Я думаю, ми могли б дозволити це, якщо розширення буде в тому ж модулі, що і протокол.
Оскільки ваш протокол знаходиться в тому ж модулі, що і ваше розширення, ви, можливо, зможете це зробити в майбутній версії Swift.
Оновлення 2
У лютому 2017 року цей випуск офіційно закрили як "Не буду робити" один із членів Команди Swift Core із таким повідомленням:
Це навмисно: розширення протоколу не можуть вводити точки входу @objc через обмеження часу виконання Objective-C. Якщо ви хочете додати точки входу @objc до NSObject, продовжте NSObject.
Розширення NSObject
або навіть UIViewController
не виконають саме те, що ви хочете, але це, на жаль, не виглядає так, що стане можливим.
У (дуже) довгостроковому майбутньому ми, можливо, зможемо повністю усунути залежність від @objc
методів, але цей час, швидше за все, не настане незабаром, оскільки какаові рамки наразі не написані в Swift (і не можуть бути, поки у нього не буде стабільної ABI) .
Оновлення 3
Станом на осінь 2019 року це стає меншою проблемою, оскільки в Swift пишеться все більше рамок Apple. Наприклад, якщо ви використовуєте SwiftUI
замість цього UIKit
, ви усунете проблему цілком, тому @objc
що ніколи не знадобиться при посиланні на SwiftUI
метод.
Рамки Apple, написані на Swift, включають:
- SwiftUI
- RealityKit
- Об’єднайте
- CryptoKit
Можна було б очікувати, що ця модель продовжиться з часом, коли Swift офіційно є ABI та модулем стабільним на Swift 5.0 та 5.1 відповідно.
@objc