Хотя я думаю, что могу ответить на ваш вопрос, это не тот ответ, который вам понравится.
TL; DR: @objc
функции в настоящее время могут отсутствовать в расширениях протокола. Вместо этого вы можете создать базовый класс, хотя это не идеальное решение.
Расширения протокола и Objective-C
Во-первых, этот вопрос / ответ ( Может ли метод Swift, определенный в расширениях для протоколов, доступных в Objective-c ), по-видимому, предполагает, что из-за того, как расширения протокола отправляются под капотом, методы, объявленные в расширениях протокола, не видны для objc_msgSend()
функции и поэтому они не видны для кода Objective-C. Поскольку метод, который вы пытаетесь определить в своем расширении, должен быть видим для Objective-C (чтобы его UIKit
можно было использовать), он кричит на вас, что вы не включаете его @objc
, но как только вы его включаете, он кричит на вас, потому что@objc
он не разрешен в расширения протокола. Вероятно, это связано с тем, что расширения протокола в настоящее время не могут быть видны Objective-C.
Мы также можем видеть, что сообщение об ошибке после добавления @objc
состояний «@objc может использоваться только с членами классов, протоколами @objc и конкретными расширениями классов». Это не класс; расширение протокола @objc - это не то же самое, что в самом определении протокола (то есть в требованиях), а слово «конкретный» предполагает, что расширение протокола не считается расширением конкретного класса.
Обходной путь
К сожалению, это практически полностью мешает вам использовать расширения протокола, когда реализации по умолчанию должны быть видимы для платформ Objective-C. Сначала я подумал, что, возможно, @objc
это не разрешено в вашем расширении протокола, потому что компилятор Swift не может гарантировать, что соответствующие типы будут классами (даже если вы специально указали UIViewController
). Итак, я поставил class
требование P1
. Это не сработало.
Возможно, единственный обходной путь здесь - просто использовать базовый класс вместо протокола, но это, очевидно, не совсем идеально, потому что класс может иметь только один базовый класс, но соответствовать нескольким протоколам.
Если вы решите пойти по этому пути, примите во внимание этот вопрос ( метод необязательного протокола Swift 3 ObjC, не вызываемый в подклассе ). Похоже, что еще одна текущая проблема в Swift 3 заключается в том, что подклассы не наследуют автоматически дополнительные реализации требований протокола своего суперкласса. Ответ на этот вопрос требует специальной адаптации, @objc
чтобы обойти это.
Сообщение о проблеме
Я думаю, что это уже обсуждается среди тех, кто работает над проектами с открытым исходным кодом Swift, но вы можете быть уверены, что они знают, используя Apple Bug Reporter , который, скорее всего, в конечном итоге дойдет до Swift Core Team, или репортера ошибок Swift . Однако любой из них может посчитать вашу ошибку слишком широкой или уже известной. Команда Swift может также рассмотреть то, что вы ищете, как новую языковую функцию, и в этом случае вам следует сначала проверить списки рассылки .
Обновить
В декабре 2016 года об этой проблеме было сообщено сообществу Swift. Проблема по-прежнему помечена как открытая со средним приоритетом, но был добавлен следующий комментарий:
Это задумано. Невозможно добавить реализацию метода для каждого пользователя, поскольку расширение может быть добавлено после соответствия протоколу. Я полагаю, мы могли бы разрешить это, если бы расширение было в том же модуле, что и протокол.
Однако, поскольку ваш протокол находится в том же модуле, что и ваше расширение, вы можете сделать это в будущей версии Swift.
Обновление 2
В феврале 2017 года один из членов основной группы Swift официально закрыл эту проблему как «Не буду» со следующим сообщением:
Это сделано намеренно: расширения протокола не могут вводить точки входа @objc из-за ограничений среды выполнения Objective-C. Если вы хотите добавить точки входа @objc в NSObject, расширьте NSObject.
Расширение NSObject
или даже UIViewController
не приведет к тому, что вы хотите, но, к сожалению, не похоже, что это станет возможным.
В (очень) долгосрочном будущем мы, возможно, сможем полностью отказаться от использования @objc
методов, но это время, скорее всего, не наступит в ближайшее время, поскольку фреймворки Какао в настоящее время не написаны на Swift (и не может быть, пока у него не будет стабильного ABI) .
Обновление 3
По состоянию на осень 2019 года это становится менее серьезной проблемой, потому что все больше и больше фреймворков Apple пишется на Swift. Например, если вы используете SwiftUI
вместо UIKit
, вы полностью обойдете проблему, потому @objc
что в этом никогда не будет необходимости при обращении к SwiftUI
методу.
Фреймворки Apple, написанные на Swift, включают:
- SwiftUI
- RealityKit
- Объединить
- CryptoKit
Можно было бы ожидать, что эта модель сохранится со временем, поскольку Swift официально является ABI и стабильным модулем в Swift 5.0 и 5.1, соответственно.
@objc