「純粋な」Swiftで弱いプロトコル参照を作成するにはどうすればよいですか(@objcなし)


561

weakprotocolがとして宣言されていない限り、参照はSwiftで機能しないよう@objcです。これは、純粋なSwiftアプリでは不要です。

このコードはコンパイルエラーを発生させます(weak非クラスタイプには適用できませんMyClassDelegate):

class MyClass {
  weak var delegate: MyClassDelegate?
}

protocol MyClassDelegate {
}

プロトコルの前にを付ける必要があります@objc。そうすると機能します。

質問:を実現する「純粋な」Swiftの方法はweak delegate何ですか?


注意してください... stackoverflow.com/a/60837041/294884
Fattie

回答:


1038

プロトコルのタイプをとして宣言する必要がありますAnyObject

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

class SomeClass {
    weak var delegate: ProtocolNameDelegate?
}

AnyObjectあなたが使用すると、クラスだけがこのプロトコルに準拠できるが、構造体または列挙型は準拠できないと言います。


25
このソリューションの私の問題は、デリゲートを呼び出すとクラッシュが発生することです-他の場所で指摘されているように、EXC_BAD_ACCESS。これはバグのようです。私が見つけた唯一の解決策は、@ objcを使用して、プロトコルからすべてのSwiftデータ型を排除することです。
ジムT 14

12
現在Swiftで弱いデリゲートを行う正しい方法は何ですか?アップルのドキュメントは示すか、彼らのコード例では弱いとしてデリゲートを宣言されていません。developer.apple.com/library/ios/documentation/swift/conceptual/...
C0D3

2
これは常に安全であるとは限りません。デリゲートへの参照も保持している場合にのみデリゲートを弱くする必要があり、その強い参照サイクルを解除する必要があることを忘れないでください。デリゲートがデリゲーターへの参照を保持していない場合、デリゲートはスコープから外れる可能性があり(弱いため)、クラッシュやその他の問題が発生します。
Trev14、18年

5
ところで、「新しいスタイル」(Swift 5)はそうすることだと思いますが、protocol ProtocolNameDelegate: AnyObject重要ではありません。
hnh

1
ある時点で非推奨になるAnyObjectので、それは必要classです。
ホセ

283

補足回答

私は、代議員が弱いべきかどうかについて常に混乱していました。最近、デリゲートとウィークリファレンスを使用するタイミングについて詳しく学びました。今後の視聴者のために、ここに補足のポイントを追加しましょう。

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

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

  • weak参照は常にオプションであり(そうでない場合はを使用unowned)、常にvar(ではないlet)を使用して、オプションnilが割り当て解除されるときにに設定できるようにします。

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

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

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

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

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

さらなる研究

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

関連した


5
これはすべて面白くて興味深いですが、私の元の質問とはあまり関係がありません。これは、弱い/ ARC自体に関するものでも、デリゲートが通常弱い理由でもありません。私たちはすでにそれらすべてについて知っており、弱いプロトコル参照を宣言する方法を疑問に思いました(@flainezによって完全に適切に回答されました)。
hnh

30
あなたが正しい。先ほどと同じ質問をしましたが、この背景情報の多くがありませんでした。上記の説明を読み、補足のメモを作成して、質問に関連するすべての問題を自分で理解できるようにしました。今、私はあなたの受け入れられた答えを適用し、なぜ私がそれをしているのかを知ることができると思います。将来の視聴者にも役立つと思います。
Suragch、2016年

5
しかし、タイプに依存しない弱いプロトコルを持つことはできますか?自身によるプロトコルは、どのオブジェクトがそれ自体に準拠しているかを気にしません。したがって、クラスまたは構造体の両方がそれに準拠できます。それでも両方に準拠できるという利点はありますが、準拠するクラス型のみが弱いのですか?
FlowUI。SimpleUITesting.com 2016

>ほとんどのデリゲートは自分が所有していないクラスを参照しているため、これを次のように書き直します:ほとんどのデリゲート。そうでない場合は、所有されていないオブジェクトが所有者になります
Victor Jalencas

36

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


7
面白い。classSwift 4.1 では非推奨ですか?
HNH

@hnhクラスを作成することで「疑似プロトコル」を作成することもできますが、プロトコル:AnyObjectは、OPが要求することをクラスを作成するよりも少ない副作用で正確に実行します。(値の型でこのようなプロトコルを使用することはまだできませんが、クラスを宣言してもそれは解決されません)
Arru

8

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

オリジナル: Obj-Cと相互運用していない場合でも、@ objcを使用するのが正しい方法です。プロトコルが列挙型や構造体ではなくクラスに適用されていることを確認します。マニュアルの「プロトコル適合性の確認」を参照してください。


前述のとおり、これはIMOであり、質問に対する回答ではありません。単純なSwiftプログラムは、NS'ismに関連付けられていない独自のプログラムに立つことができるはずです(これは、デリゲートを使用しないことを意味するかもしれませんが、他の設計構成を使用する可能性があります)。私の純粋なSwift MyClassは、実際には宛先が構造体なのかオブジェクトなのかを気にしませんし、オプションも必要としません。多分彼らは後でそれを修正するようになります、結局それは新しい言語です。参照セマンティクスが必要な場合、おそらく「クラスプロトコルXYZ」のようなものですか?
2014年

4
\ @objcには追加の副作用があることにも注目する価値があると思います。@ eXhaustedのNSObjectProtocolの提案は少し優れています。\ @objcを使用-クラスデリゲートが 'handleResult(r:MySwiftResultClass)'のようなオブジェクト引数を取る場合、MySwiftResultClassはNSObjectから継承する必要があります!そしておそらくそれはもう名前空間も使われていない、などです。要するに、\ @ objcはブリッジ機能であり、言語機能ではありません。
2014年

彼らはこれを解決したと思います。次のように記述します。protocol MyClassDelegate:class {}
user3675131

これに関するドキュメントはどこにありますか?私はこれについての情報を見つけることができないので、私は、盲目だか何かの間違っているのどちらか... O_O
BastiBen

OPの質問に答えるかどうかはわかりませんが、これは特にObjc-Cと相互運用している場合に役立ちます;)
Dan Rosenstark

-1

プロトコルはAnyObject、クラスのサブクラスでなければなりません

以下の例

    protocol NameOfProtocol: class {
   // member of protocol
    }
   class ClassName: UIViewController {
      weak var delegate: NameOfProtocol? 
    }

-9

Appleは「クラス」の代わりに「NSObjectProtocol」を使用します。

public protocol UIScrollViewDelegate : NSObjectProtocol {
   ...
}

これは私にとっても機能し、自分のデリゲートパターンを実装しようとしたときに発生していたエラーを取り除きました。


5
質問には関係ありません。この質問は、デリゲートオブジェクトをサポートする純粋なSwiftクラス(特にNSObject なし)の構築に関するものです。Objective-Cプロトコルを実装することではありません。後者には、@ objc、つまりNSObjectProtocolが必要です。
hnh

大丈夫ですが、お勧めできません。
DawnSong 2016
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.