weak
の参照は、protocol
が@objc
として宣言されていない限り、Swiftでは機能しないようです。これは純粋なSwiftアプリケーションでは必要ありません。
このコードはコンパイルエラーになります(weak
は非クラス型MyClassDelegate
には適用できません)。
class MyClass {
weak var delegate: MyClassDelegate?
}
protocol MyClassDelegate {
}
プロトコルの前に@objc
を付ける必要があります。それでうまくいきます。
質問:weak
delegate
を達成するための「純粋な」Swiftの方法は何ですか?
プロトコルの型をclass
として宣言する必要があります。
protocol ProtocolNameDelegate: class {
// Protocol stuff goes here
}
class SomeClass {
weak var delegate: ProtocolNameDelegate?
}
私の理解するところでは、class
を使うことで、このプロトコルはクラスだけに使用され、列挙型や構造体のような他のものには使用されないということです。
私は、デリゲートを弱くすべきかどうかについて、常に混乱していました。最近、デリゲートと弱い参照を使用するタイミングについてより多くを学んだので、将来の視聴者のために、ここに補足ポイントを追加します。
weak
キーワードを使用する目的は、 強参照サイクル (サイクルの保持)を回避することです。強参照サイクルは、2つのクラスインスタンスが相互に強参照を持つ場合に発生します。参照カウントがゼロになることはないため、割り当てが解除されることはありません。
デリゲートがクラスの場合のみ、weak
を使用する必要があります。 Swift構造体と列挙型は値型(新しいインスタンスが作成されるときに値がコピーされる)であり、参照型ではないため、強い参照を作成しませんサイクル。
weak
参照は常にオプションで(それ以外の場合はunowned
を使用します)、常にvar
(let
ではなく)を使用して、オプションをnil
に設定できるようにします割り当て解除されます。
親クラスは当然、子クラスへの強い参照を持つ必要があるため、weak
キーワードを使用しないでください。ただし、子が親への参照が必要な場合は、weak
キーワードを使用して、親を弱参照にする必要があります。
weak
は、親を参照する子だけでなく、所有していないクラスへの参照が必要な場合に使用する必要があります。 2つの非階層クラスが相互に参照する必要がある場合は、弱いクラスを選択します。選択するものは状況によって異なります。詳細については、 この質問 の回答を参照してください。
原則として、ほとんどのデリゲートは自分が所有していないクラスを参照しているため、デリゲートはweak
としてマークする必要があります。これは、子がデリゲートを使用して親と通信している場合に間違いなく当てはまります。デリゲートに弱い参照を使用することは、 documentation が推奨するものです。 (ただし、 this も参照してください。)
プロトコルは 参照型 (クラス)と 値型 (構造体、列挙型)の両方に使用できます。そのため、デリゲートを弱くする必要がある可能性が高い場合、オブジェクト専用プロトコルにする必要があります。その方法は、プロトコルの継承リストにAnyObject
を追加することです。 (過去にclass
キーワードを使用してこれを実行しましたが、 AnyObject
が現在推奨されています 。)
protocol MyClassDelegate: AnyObject {
// ...
}
class SomeClass {
weak var delegate: MyClassDelegate?
}
次の記事を読むことで、これをよりよく理解することができました。また、unowned
キーワードなどの関連する問題や、クロージャーで発生する強力な参照サイクルについても説明します。
AnyObject
はSwiftで弱い参照を使うための公式な方法です。
class MyClass {
weak var delegate: MyClassDelegate?
}
protocol MyClassDelegate: AnyObject {
}
アップルから:
強い参照サイクルを防ぐために、デリゲートは弱い参照として宣言されるべきです。弱参照の詳細については、「クラスインスタンス間の厳密な参照サイクル」を参照してください。プロトコルをクラス専用としてマークすると、後でデリゲートが弱い参照を使用する必要があることを宣言できます。クラスオンリープロトコルで説明したように、 AnyObject から継承することで、プロトコルをクラスオンリーとしてマークします。
更新: マニュアルが更新され、私が言及していた例が削除されたようです。上記の@ flainezの回答の編集を参照してください。
オリジナル: @ Obj-Cと相互運用していなくても@objcを使うのが正しい方法です。それはあなたのプロトコルがクラスやenumやstructではなくクラスに適用されていることを保証します。マニュアルの「プロトコルの適合性の確認」を参照してください。