web-dev-qa-db-ja.com

Swift 4で#selector()を使って@objc推論の非推奨を処理する方法を教えてください。

私は自分のプロジェクトのソースコードをSwift 3からSwift 4に変換しようとしています。Xcodeから警告が出ます。

たとえば、次のような通常のセレクタを使用してボタンにターゲットを追加します。

button.addTarget(self, action: #selector(self.myAction), for: .touchUpInside)

これはそれが示す警告です:

'#selector'の引数は、 'ViewController'内のインスタンスメソッド 'myAction()'を参照します。これは '@objc'属性推論に依存しており、Swift 4では廃止予定です。

このインスタンスメソッドをObjective-Cに公開するには、 '@ objc'を追加します。

さて、エラーメッセージのFixを押すことは私の関数にこれをします:

// before
func myAction() { /* ... */ }

// after
@objc func myAction() { /* ... */ }

私は本当に@objcマークを含むように私のすべての関数の名前を変更したくありません、そして、私はそれが必要ではないと思います。

非推奨に対処するためにどうやってセレクタを書き直すのですか?


関連質問:

127
LinusGeffarth

修正は正しいです。セレクタが参照するメソッドをObjective-Cに公開するために変更できるセレクタについては何もありません。

そもそもこの警告の全体的な理由は、 SE-016 の結果です。 Swift 4より前は、internal以上のObjective-C互換クラスのNSObject継承クラスは@objcであると推測されたため、Objective-Cに公開されていたため、それらはセレクターを使用して呼び出されます(特定のセレクターのメソッド実装を検索するにはObj-Cランタイムが必要です)。

ただし、Swift 4では、これは当てはまりません。 @objcメソッドのオーバーライド、@objcプロトコル要件の実装、@objcを暗示する属性を持つ宣言など、非常に具体的な宣言のみが@objcであると推測されるようになりました。 @IBOutlet

上記のリンクされた提案で のように、この背後にある動機は、最初にNSObject継承クラスのメソッドのオーバーロードが同一のセレクターを持つために互いに衝突するのを防ぐことです。第二に、Obj-Cにさらされる必要のないメンバーのサンクを生成する必要がないため、バイナリサイズを削減でき、第三に、動的リンクの速度が向上します。

メンバーをObj-Cに公開する場合は、次のように@objcとしてマークする必要があります。

class ViewController: UIViewController {

    @IBOutlet weak var button: UIButton!

    override func viewDidLoad() {
        super.viewDidLoad()
        button.addTarget(self, action: #selector(foo), for: .touchUpInside)
    }

    @objc func foo() {
       // ... 
    }
}

(「minimise inference」オプションが選択された状態で実行されている場合、マイグレーターはセレクターでこれを自動的に行う必要があります)

メンバーのグループをObj-Cに公開するには、@objc extensionを使用できます。

@objc extension ViewController {

    // both exposed to Obj-C
    func foo() {}
    func bar() {}
}

これにより、定義されているすべてのメンバーがObj-Cに公開され、Obj-Cに公開できないメンバーにエラーが発生します(明示的に@nonobjcとマークされている場合を除く)。

all Obj-Cと互換性のあるメンバーをObj-Cに公開する必要があるクラスがある場合、クラスを@objcMembersとしてマークできます。

@objcMembers
class ViewController: UIViewController {
   // ...
}

これで、@objcであると推測できるすべてのメンバーがそうなります。ただし、メンバーが不必要に公開されるという上記の欠点を考えると、本当に Obj-Cに公開されているすべてのメンバーが必要でない限り、これを行うことはお勧めしません。

151
Hamish

As Apple Official Documentation として。 Selectorメソッドを呼び出すには@objcを使用する必要があります。

Objective-Cでは、セレクタはObjective-Cメソッドの名前を参照する型です。 Swiftでは、Objective-CのセレクタはSelector構造体で表され、#selector式を使用して構築できます。 Objective-Cから呼び出せるメソッドのセレクタを作成するには、#selector(MyViewController.tappedButton(sender:))のようにメソッドの名前を渡します。プロパティのObjective-Cのgetterメソッドまたはsetterメソッドのセレクタを作成するには、#selector(getter: MyViewController.myButton)のように、getter:またはsetter:というラベルを前に付けたプロパティ名を渡します。

16
Kiran Sarvaiya

Swift 4.2では、@ IBActionをメソッドに代入するだけでよいので、この愚かな@objcアノテーションを避けることができます。

`` `

let tap  =  UITapGestureRecognizer(target: self, action: #selector(self.cancel))


@IBAction func cancel()
{
    self.dismiss(animated: true, completion: nil)
}
9
Logan Sease

他の答えで既に述べたように、セレクターの@objcアノテーションを避ける方法はありません。

ただし、OPに記載されている警告は、次の手順を実行することで沈黙させることができます。

  1. ビルド設定に移動します。
  2. キーワードで検索@ objc
  3. Swift 3 @objcインターフェースの値をOffに設定します

以下は上記の手順を説明するスクリーンショットです。

Silencing the warning "Swift 3 @objc interface"

お役に立てれば

1
S1LENT WARRIOR

ビューコントローラにObjective Cのメンバが必要な場合は、ビューの上部に@ objcMembersを追加するだけです。コントローラ。コードにIBActionを追加することでこれを回避できます。

@IBAction func buttonAction() {

}

ストーリーボードでこのコンセントを必ず接続してください。

1
Renjish C