Ответ 1
Хотя я думаю, что смогу ответить на ваш вопрос, он вам не понравится.
Функции 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 bug reporter. Однако любой из них может найти вашу ошибку слишком широкой или уже известной. Команда Swift может также рассмотреть то, что вы ищете, как новую языковую функцию, и в этом случае вы должны сначала проверить списки рассылки.
Обновление
В декабре 2016 года об этой проблеме было сообщено сообществу Swift. Проблема по-прежнему помечена как открытая со средним приоритетом, но добавлен следующий комментарий:
Это предназначено. Невозможно добавить реализацию метода для каждого пользователя, так как расширение может быть добавлено после соответствия протоколу. Я полагаю, что мы могли бы разрешить это, если расширение находится в том же модуле, что и протокол.
Поскольку ваш протокол находится в том же модуле, что и ваше расширение, вы, возможно, сможете сделать это в будущей версии Swift.
Обновление 2
В феврале 2017 года этот вопрос был официально закрыт одним из членов команды Swift Core со статусом "не будет" со следующим сообщением:
Это сделано намеренно: расширения протокола не могут вводить точки входа @objc из-за ограничений времени выполнения Objective C. Если вы хотите добавить точки входа @objc в NSObject, расширьте NSObject.
Расширение NSObject
или даже UIViewController
не будет выполнять именно то, что вы хотите, но, к сожалению, не похоже, что это станет возможным.
В (очень) долгосрочном будущем мы, возможно, сможем полностью исключить зависимость от методов @objc
, но это время, скорее всего, не наступит в ближайшее время, так как фреймворки Cocoa в настоящее время не написаны на языке Swift (и не могут быть, пока у него нет стабильный ABI).
Обновление 3
По состоянию на осень 2019 года это становится меньшей проблемой, потому что все больше и больше фреймворков Apple пишутся на Swift. Например, если вы используете SwiftUI
вместо UIKit
, вы полностью обойдете проблему, потому что @objc
никогда не понадобится при обращении к методу SwiftUI
.
Основы Apple, написанные на Swift, включают:
- SwiftUI
- RealityKit
- Объединить
- CryptoKit
Можно было бы ожидать, что эта модель сохранится со временем, теперь, когда Swift официально ABI и стабильный модуль, начиная с Swift 5.0 и 5.1, соответственно.