web-dev-qa-db-ja.com

どうやって(純粋な)Swiftで弱いプロトコル参照をすることができますか(@objcなしで)

weakの参照は、protocol@objcとして宣言されていない限り、Swiftでは機能しないようです。これは純粋なSwiftアプリケーションでは必要ありません。

このコードはコンパイルエラーになります(weakは非クラス型MyClassDelegateには適用できません)。

class MyClass {
  weak var delegate: MyClassDelegate?
}

protocol MyClassDelegate {
}

プロトコルの前に@objcを付ける必要があります。それでうまくいきます。

質問:weakdelegateを達成するための「純粋な」Swiftの方法は何ですか?

520
hnh

プロトコルの型をclassとして宣言する必要があります。

protocol ProtocolNameDelegate: class {
    // Protocol stuff goes here
}

class SomeClass {
    weak var delegate: ProtocolNameDelegate?
}

私の理解するところでは、classを使うことで、このプロトコルはクラスだけに使用され、列挙型や構造体のような他のものには使用されないということです。

958
flainez

補足回答

私は、デリゲートを弱くすべきかどうかについて、常に混乱していました。最近、デリゲートと弱い参照を使用するタイミングについてより多くを学んだので、将来の視聴者のために、ここに補足ポイントを追加します。

  • weakキーワードを使用する目的は、 強参照サイクル (サイクルの保持)を回避することです。強参照サイクルは、2つのクラスインスタンスが相互に強参照を持つ場合に発生します。参照カウントがゼロになることはないため、割り当てが解除されることはありません。

  • デリゲートがクラスの場合のみ、weakを使用する必要があります。 Swift構造体と列挙型は値型(新しいインスタンスが作成されるときに値がコピーされる)であり、参照型ではないため、強い参照を作成しませんサイクル。

  • weak参照は常にオプションで(それ以外の場合はunownedを使用します)、常にvarletではなく)を使用して、オプションをnilに設定できるようにします割り当て解除されます。

  • 親クラスは当然、子クラスへの強い参照を持つ必要があるため、weakキーワードを使用しないでください。ただし、子が親への参照が必要な場合は、weakキーワードを使用して、親を弱参照にする必要があります。

  • weakは、親を参照する子だけでなく、所有していないクラスへの参照が必要な場合に使用する必要があります。 2つの非階層クラスが相互に参照する必要がある場合は、弱いクラスを選択します。選択するものは状況によって異なります。詳細については、 この質問 の回答を参照してください。

  • 原則として、ほとんどのデリゲートは自分が所有していないクラスを参照しているため、デリゲートはweakとしてマークする必要があります。これは、子がデリゲートを使用して親と通信している場合に間違いなく当てはまります。デリゲートに弱い参照を使用することは、 documentation が推奨するものです。 (ただし、 this も参照してください。)

  • プロトコルは 参照型 (クラス)と 値型 (構造体、列挙型)の両方に使用できます。そのため、デリゲートを弱くする必要がある可能性が高い場合、オブジェクト専用プロトコルにする必要があります。その方法は、プロトコルの継承リストにAnyObjectを追加することです。 (過去にclassキーワードを使用してこれを実行しましたが、 AnyObjectが現在推奨されています 。)

    protocol MyClassDelegate: AnyObject {
        // ...
    }
    
    class SomeClass {
        weak var delegate: MyClassDelegate?
    }
    

さらなる研究

次の記事を読むことで、これをよりよく理解することができました。また、unownedキーワードなどの関連する問題や、クロージャーで発生する強力な参照サイクルについても説明します。

関連する

258
Suragch

AnyObjectはSwiftで弱い参照を使うための公式な方法です。

class MyClass {
    weak var delegate: MyClassDelegate?
}

protocol MyClassDelegate: AnyObject {
}

アップルから:

強い参照サイクルを防ぐために、デリゲートは弱い参照として宣言されるべきです。弱参照の詳細については、「クラスインスタンス間の厳密な参照サイクル」を参照してください。プロトコルをクラス専用としてマークすると、後でデリゲートが弱い参照を使用する必要があることを宣言できます。クラスオンリープロトコルで説明したように、 AnyObject から継承することで、プロトコルをクラスオンリーとしてマークします。

https://developer.Apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html#//Apple_ref/doc/uid/TP40014097-CH25-ID276

28
Tim Chen

更新: マニュアルが更新され、私が言及していた例が削除されたようです。上記の@ flainezの回答の編集を参照してください。

オリジナル: @ Obj-Cと相互運用していなくても@objcを使うのが正しい方法です。それはあなたのプロトコルがクラスやenumやstructではなくクラスに適用されていることを保証します。マニュアルの「プロトコルの適合性の確認」を参照してください。

9
William Rust