Обзор:
- У меня есть протокол 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 {
}
swift
swift3
swift-protocols
user1046037
источник
источник
@objc
Ответы:
Хотя я думаю, что могу ответить на ваш вопрос, это не тот ответ, который вам понравится.
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 официально закрыл эту проблему как «Не буду» со следующим сообщением:
Расширение
NSObject
или дажеUIViewController
не приведет к тому, что вы хотите, но, к сожалению, не похоже, что это станет возможным.В (очень) долгосрочном будущем мы, возможно, сможем полностью отказаться от использования
@objc
методов, но это время, скорее всего, не наступит в ближайшее время, поскольку фреймворки Какао в настоящее время не написаны на Swift (и не может быть, пока у него не будет стабильного ABI) .Обновление 3
По состоянию на осень 2019 года это становится менее серьезной проблемой, потому что все больше и больше фреймворков Apple пишется на Swift. Например, если вы используете
SwiftUI
вместоUIKit
, вы полностью обойдете проблему, потому@objc
что в этом никогда не будет необходимости при обращении кSwiftUI
методу.Фреймворки Apple, написанные на Swift, включают:
Можно было бы ожидать, что эта модель сохранится со временем, поскольку Swift официально является ABI и стабильным модулем в Swift 5.0 и 5.1, соответственно.
источник
Swift 4
нет другой альтернативы.Я столкнулся с этим только после включения «стабильности модуля» (включения «Сборка библиотек для распространения») в используемой мной быстрой среде.
У меня было что-то вроде этого:
Функция в расширении имела следующие ошибки:
Метод экземпляра '@objc' в расширении подкласса 'LessAwesomeClass' требует iOS 13.0.0
Не - '@ objc' метод niceDelegateFunc 'не удовлетворяет требованиям протокола' @objc 'GreatDelegate
Перемещение функций в класс, а не в расширение, решило проблему.
источник
Вот еще одно решение. Я тоже столкнулся с этой проблемой и пока не могу переключиться с UIKit на SwiftUI. Перемещение реализаций по умолчанию в общий базовый класс тоже не было вариантом для меня. Мои реализации по умолчанию были довольно обширными, поэтому я действительно не хотел дублировать весь этот код. Обходной путь, который я в итоге использовал, заключался в использовании функций-оберток в протоколе, а затем просто вызов этих функций из каждого класса. Не очень красиво, но может быть лучше, чем альтернатива, в зависимости от ситуации. Тогда ваш код будет выглядеть примерно так:
источник