「@objc」以外のメソッドは、「@ objc」プロトコルのオプションの要件を満たしていません


103

概要:

  • 私はObjective-Cオプション機能の1つのデフォルト実装を提供するプロトコルP1を持っています。
  • オプション機能のデフォルト実装を提供すると警告が表示されます

コンパイラの警告:

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

バージョン:

  • スウィフト: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 {

}

Xcodeの最新バージョンをお持ちですか?削除してもエラーは発生しません@objc
Qbyte

Xcode 8(最新の公開バージョン)を使用しています。エラーはありませんが、警告が表示されます
user1046037

回答:


182

私はあなたの質問に答えることはできると思いますが、あなたが好きな答えではありません。

TL; DR: @objc関数は現在プロトコル拡張にない場合があります。代わりに基本クラスを作成することもできますが、これは理想的なソリューションではありません。

プロトコル拡張とObjective-C

まず、この質問/回答(Objective-cでアクセスされるプロトコルの拡張で定義されたCan Swiftメソッド)は、プロトコル拡張が内部でディスパッチされる方法が原因で、プロトコル拡張で宣言されたメソッドが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のもう1つの現在の問題は、サブクラスがスーパークラスのオプションのプロトコル要件の実装を自動的に継承しないことです。その質問への答えは、@objcそれを回避するための特別な適応を使用しています。

問題の報告

私はこれがスウィフトのオープンソースプロジェクトに取り組むものの中に、既に議論されていると思いますが、あなたは彼らが使用していずれかの方法で認識している可能性があり、Appleのバグレポーターそう結局スウィフトコアチームへの道になるだろう、またはスウィフトのバグ報告。ただし、これらのどちらを使用しても、バグが広すぎるか、すでにわかっている場合があります。Swiftチームは、探しているものを新しい言語機能と見なす場合もあります。その場合は、最初にメーリングリストを確認する必要があります

更新

2016年12月、この問題 Swiftコミュニティに報告されました。この問題は未だ優先度が中のオープンとしてマークされていますが、次のコメントが追加されました。

これは意図されたものです。プロトコルへの準拠後に拡張機能を追加できるため、すべての採用者にメソッドの実装を追加する方法はありません。ただし、拡張機能がプロトコルと同じモジュール内にある場合は、それを許可できると思います。

ただし、プロトコルは拡張機能と同じモジュール内にあるため、Swiftの将来のバージョンでこれを実行できる可能性があります。

アップデート2

2017年2月、この問題、Swiftコアチームメンバーの1人によって「Wo n't Do」として次のメッセージで正式にクローズされました。

これは意図的なものです。Objective-Cランタイムの制限により、プロトコル拡張では@objcエントリポイントを導入できません。@objcエントリポイントをNSObjectに追加する場合は、NSObjectを拡張します。

拡張しNSObjectたりUIViewController、あなたが望むものを正確に達成することはできませんが、残念ながらそれは可能になるようには見えません。

(非常に)長期的には、@objcメソッドへの依存を完全になくすことができるかもしれませんが、Cocoaフレームワークは現在Swiftで記述されていないため(そして、安定したABIになるまでは、そうなることはないため) 。

アップデート3

2019年秋以降、Swiftで記述されるAppleフレームワークが増えているため、この問題の問題は少なくなっています。たとえば、のSwiftUI代わりにを使用するとUIKit、メソッド@objcを参照するときに必要になることはないため、問題を完全に回避できますSwiftUI

Swiftで記述されたAppleフレームワークには次のものがあります。

  • SwiftUI
  • RealityKit
  • 組み合わせる
  • CryptoKit

Swiftが正式にABIであり、モジュールがSwift 5.0と5.1でそれぞれ安定しているため、このパターンは今後も継続すると予想されます。


1
@ user1046037私も同じです。将来の開発で何度もこの問題に遭遇しているのを見ることができます。
Matthew Seaman 2016

2
あなたは正しいSwift 4です。今のところ他の選択肢はありませんが、あなたの答えはまだ有効です。
user1046037 2017

1
まったく同じコードがしばらくの間機能していましたが、Xcodeの今後のリリースで問題が発生しました。これはかなり参考になります。Objective-Cプロトコルには非常に多くのオプションのメソッドがあります。
デパルタメントB

0

私は、使用する迅速なフレームワークで「モジュールの安定性」を有効にした後(「配布用にライブラリをビルド」をオンにした後)、これに遭遇しました。

私が持っていたのはこのようなものでした:

class AwesomeClass: LessAwesomeClass {
...
}

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

拡張機能の関数に次のエラーがありました:

  • 「LessAwesomeClass」のサブクラスを拡張した「@objc」インスタンスメソッドにはiOS 13.0.0が必要です

  • '@objc'以外のメソッド 'niceDelegateFunc'は '@objc'プロトコル 'GreatDelegate'の要件を満たしていません

拡張機能ではなくクラスに関数を移動すると、問題が解決しました。


0

ここに別の回避策があります。私もこの問題に遭遇しましたが、まだUIKitからSwiftUIに切り替えることはできません。デフォルトの実装を共通の基本クラスに移動することも、私にとって選択肢ではありませんでした。私のデフォルトの実装は非常に広範だったので、コードをすべて複製したくありませんでした。最終的に使用した回避策は、プロトコルでラッパー関数を使用して、各クラスからこれらの関数を呼び出すだけでした。きれいではありませんが、状況によっては他の方法よりも優れている場合があります。コードは次のようになります。

@objc protocol P1 : UIAdaptivePresentationControllerDelegate {
}

extension P1 where Self : UIViewController {
    func wrapPresentationController(_ controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? {
        return UIViewController()
    }
}

class A : UIViewController, P1 {
    func presentationController(_ controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? {
        return wrapPresentationController(controller, viewControllerForAdaptivePresentationStyle: style)
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.