Подтвердить что ты не робот

Метод 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 {

}
4b9b3361

Ответ 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, соответственно.

Ответ 2

Я только что столкнулся с этим после включения "стабильности модуля" (включения "Сборки библиотек для распространения") в быстрой среде, которую я использую.

У меня было что-то вроде этого:

class AwesomeClass: LessAwesomeClass {
...
}

extension AwesomeClass: GreatDelegate {
  func niceDelegateFunc() {
  }
}

Функция в расширении имела следующие ошибки:

  • Метод экземпляра '@objc' в расширении подкласса 'LessAwesomeClass' требует iOS 13.0.0

  • Non- Метод '@objc' 'niceDelegateFunc' не удовлетворяет требованию протокола '@objc' 'GreatDelegate'

Перемещение функций в класс, а не в расширение, решило проблему.