1. ホーム
  2. swift

[解決済み] Swift 4の#selector()で@objc推論の非推奨にどう対処すればいいですか?

2022-06-29 21:28:56

質問

私は自分のプロジェクトのソースコードをSwift 3からSwift 4に変換しようとしています。Xcodeが私に与えている1つの警告は、私のセレクタについてです。

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

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

このような警告が表示されます。

selector' の引数は Swift 4 で非推奨となった '@objc' 属性推論に依存する 'ViewController' のインスタンスメソッド 'myAction()' を参照しています。

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

では Fix を押すと、私の関数ではこのようになります。

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

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

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

非推奨に対応するために、セレクタをどのように書き換えたらよいでしょうか。


関連する質問です。

どのように解決するのですか?

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

そもそもこの警告が発生する理由は、すべて SE-0160 . Swift 4 より前の internal またはそれ以上の Objective-C 互換のメンバである NSObject を継承するクラスは @objc であると推測され、したがってObjective-Cに公開されるため、セレクタを使用して呼び出すことができます(与えられたセレクタのメソッド実装を検索するためにObj-Cランタイムが必要であるため)。

しかし、Swift 4では、これはもはやそうではありません。非常に特定の宣言のみが、現在では @objc であると推測されます。 @objc メソッドの実装 @objc プロトコルの要件、およびその属性を持つ宣言を意味する @objc を意味する属性を持つ宣言、例えば @IBOutlet .

この背後にある動機は、詳細な で、上記のリンク先の提案 におけるメソッドのオーバーロードを防ぐためです。 NSObject を継承するクラスのメソッドのオーバーロードが、同一のセレクタを持つために互いに衝突することを防ぐためです。次に、Obj-Cに公開する必要のないメンバのサンクを生成する必要がないため、バイナリサイズを小さくすることができ、3番目にダイナミックリンクの速度を向上させることができます。

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 ).

が必要なクラスがある場合 全て Obj-C互換のメンバがObj-Cに公開される必要がある場合、そのクラスを @objcMembers :

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

これで、推論可能なすべてのメンバが @objc になります。しかし、以下のような場合でなければ、この方法はお勧めできません。 本当に はObj-Cにすべてのメンバーを公開する必要がありますが、不必要にメンバーを公開することによる上記のマイナス面を考えると、これを行うことをお勧めしません。