Метод Non-‘@ objc’ не удовлетворяет необязательным требованиям протокола @objc

Обзор:

  • У меня есть протокол P1, который обеспечивает реализацию по умолчанию одной из необязательных функций Objective-C.
  • Когда я предоставляю стандартную реализацию дополнительной функции, появляется предупреждение

Предупреждение компилятора:

Non-'@objc' method 'presentationController(_:viewControllerForAdaptivePresentationStyle:)' does not satisfy optional requirement of '@objc' protocol 'UIAdaptivePresentationControllerDelegate' 

Версия:

  • Swift: 3
  • Xcode: 8 (публичный выпуск)

Попытки:

  • Попробовал добавить @objc но не помог

Вопрос:

  • Как я могу это решить?
  • Есть ли работа?

Код:

 @objc protocol P1 : UIAdaptivePresentationControllerDelegate { } extension P1 where Self : UIViewController { func presentationController(_ controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? { return UIViewController() } } class A : UIViewController, P1 { } 

Хотя я думаю, что могу ответить на ваш вопрос, это не ответ, который вам понравится.

TL; DR: функции @objc настоящее время не могут быть в расширениях протокола. Вместо этого вы можете создать базовый class, хотя это не идеальное решение.

Расширения протокола и Objective-C

Во-первых, этот вопрос / ответ ( метод Swift, определенный для расширений для протоколов, доступных в Objective-c ), по-видимому, предполагает, что из-за способа отправки расширений протокола под капотом методы, объявленные в расширениях протокола, не видны objc_msgSend() и поэтому не отображаются в коде Objective-C. Поскольку метод, который вы пытаетесь определить в своем расширении, должен быть видимым для Objective-C (поэтому UIKit может его использовать), он кричит на вас, чтобы он не включал @objc , но как только вы @objc его, он кричит на вас, потому что @objc не допускается в расширениях протокола. Вероятно, это связано с тем, что в Objective-C расширения протокола в настоящее время не видны.

Мы также видим, что сообщение об ошибке после добавления состояний @objc «@objc может использоваться только с членами classов, протоколами @objc и конкретными расширениями classов». Это не class; расширение к протоколу @objc не совпадает с самим определением протокола (т.е. в требованиях), а слово «конкретный» предполагает, что расширение протокола не считается конкретным расширением classа.

Временное решение

К сожалению, это в значительной степени полностью предотвращает использование расширений протокола, когда реализации по умолчанию должны быть видны в рамках Objective-C. Сначала я подумал, что, возможно, @objc не разрешен в расширении вашего протокола, потому что Swift Compiler не может гарантировать, что соответствующие типы будут classами (даже если вы специально указали UIViewController ). Поэтому я поставил требование class на P1 . Это не сработало.

Возможно, единственным обходным решением является просто использовать базовый class вместо протокола здесь, но это, очевидно, не совсем идеально, потому что class может иметь только один базовый class, но соответствовать нескольким протоколам.

Если вы решите пойти по этому маршруту, задайте этот вопрос ( Swift 3 ObjC Необязательный метод протокола, не вызываемый в подclassе ). Похоже, что еще одна текущая проблема в Swift 3 заключается в том, что подclassы не наследуют необязательные реализации требований к их суперclassам. Ответ на эти вопросы использует специальную адаптацию @objc чтобы обойти ее.

Отчет по проблеме

Я думаю, что это обсуждается уже среди тех, кто работает над проектами с открытым исходным кодом Swift, но вы можете быть уверены, что им известно, что они используют Apple Bug Reporter , который, вероятно, в конечном итоге попадет в команду Swift Core Team или Swift . Однако любой из них может найти вашу ошибку слишком широкой или уже известной. Команда Swift также может рассмотреть то, что вы ищете, чтобы быть новой языковой функцией, и в этом случае вы должны сначала проверить списки рассылки .

Обновить

В декабре 2016 года этот вопрос был сообщен сообществу Swift. Проблема по-прежнему отмечена как открытая со средним приоритетом, но был добавлен следующий комментарий:

Это предназначено. Невозможно добавить реализацию метода каждому усыновителю, поскольку расширение может быть добавлено после соответствия протоколу. Я предполагаю, что мы могли бы позволить это, если расширение находится в том же модуле, что и протокол.

Однако, поскольку ваш протокол находится в том же модуле, что и расширение, вы можете сделать это в будущей версии Swift.

Обновление 2

В феврале 2017 года этот вопрос был официально закрыт как «Не будет» одним из членов команды Swift Core Team со следующим сообщением:

Это преднамеренно: расширения протокола не могут вводить точки входа @objc из-за ограничений времени выполнения Objective-C. Если вы хотите добавить точки входа @objc в NSObject, добавьте NSObject.

Расширение NSObject или даже UIViewController не будет выполнять именно то, что вы хотите, но, к сожалению, не похоже, что это станет возможным.

В (очень) долгосрочном будущем мы можем полностью избавиться от методов @objc , но это время, скорее всего, не придет в ближайшее время, так как каркасы Cocoa в настоящее время не записаны в Swift (и не могут быть до тех пор, пока он не стабилизируется ABI).

Давайте будем гением компьютера.