なぜObjective-Cのデリゲートは通常、保持ではなくプロパティの割り当てを与えられるのですか?


176

私はスコットスティーブンソンによって管理されている素晴らしいブログをサーフィンしていて、代理人に「割り当て」プロパティと「保持」を割り当てるという基本的なObjective-Cの概念を理解しようとしています。ガベージコレクションされた環境では、どちらも同じであることに注意してください。私は主に非GCベースの環境(例:iPhone)に関心があります。

スコットのブログから直接:

「assignキーワードは、値をコピーまたは保持するのではなく、インスタンス変数に直接値を割り当てるセッターを生成します。これは、NSIntegerやCGFloatなどのプリミティブ型、またはデリゲートなどの直接所有していないオブジェクトに最適です。」

デリゲートオブジェクトを直接所有していないとはどういう意味ですか?私は通常、デリゲートを保持します。なぜなら、彼らが奈落の底に逃げたくない場合は、保持がそれを処理してくれるからです。私は通常、UITableViewControllerをそれぞれのdataSourceおよびデリゲートから分離して抽象化します。その特定のオブジェクトも保持します。UITableViewには常にデリゲートがあるので、それが消えないようにしたいと思います。

誰かがどこで/なぜ私が間違っているのかさらに説明できるので、Objective-C 2.0プログラミングで、保持の代わりにデリゲートで割り当てプロパティを使用するこの一般的なパラダイムを理解できますか?

ありがとう!


「代理人」で「iphone」なしでタグ付けし直しました。
クイン・テイラー、

コピーではなくデリゲートが割り当てられる理由(NSStringなど)
OMGPOP

回答:


175

デリゲートの保持を回避する理由は、保持サイクルを回避する必要があるためです。

AがBを作成AがBの代理人として自分自身を設定…Aはその所有者によって解放されます

BがAを保持していた場合、BはAを所有しているため、Aは解放されません。したがって、Aの割り当て解除は呼び出されず、AとBの両方がリークします。

AがBを所有しているため、deallocでAが削除されるため、Aがなくなることを心配する必要はありません。


同意しません、マイク。モーダルがモーダルを閉じるデリゲートを持っているという問題を見つけました。しかし、モーダルでメモリ警告を行うと、デリゲートが解放されます。次に、モーダルを閉じようとすると、デリゲートはnilになります。クラッシュ。
ポールシャピロ

わかりましたので、私は同意しませんが、それは設計上の欠陥だとあなたは正しいです。私は本当の却下者の子クラスを介して却下の電話を渡していることがわかりました。その子クラスが解放され、モーダルのコンテナーデリゲートに渡して閉じることができませんでした。最後のデリゲートへのポインタを渡すように変更しましたが、メモリ警告で解放されず、すべて正常です。
ポールシャピロ

2
あなたのコードは、nilデリゲートがそれをクラッシュさせるような方法で書かれるべきではありません。所有しているオブジェクトだけが所有している参照を持つ必要があります。割り当てを解除するときは、所有オブジェクトのデリゲートを解放する前にnilに設定する必要があります。その後、nilデリゲートに送信されたメッセージはすべて無視されます。ただし、メッセージでnilオブジェクト渡すとクラッシュする可能性があります。デリゲートをそのように扱わないようにしてください。
デビッドギッシュ

待ってください-それは何をweakしていませんか?問題はなぜassign代わりに使用するのweakですか?
wcochran 2013年

3
@wcochran:いいえ、この質問はのassign代わりに使用する理由ですretain。質問はARCより古いものです。weakそして、strong(と後者の同義retainARCが導入されるまで)には存在しませんでした。weak対については、assign個別に質問する必要があります。
Peter Hosey 2013年

44

デリゲートメッセージを送信するオブジェクトはデリゲートを所有していないためです。

コントローラがビューまたはウィンドウのデリゲートとして自分自身を設定する場合など、多くの場合、それはその逆です。コントローラはビ​​ュー/ウィンドウを所有しているため、ビュー/ウィンドウがそのデリゲートを所有している場合、両方のオブジェクトが互いに所有します。もちろん、これは保持サイクルであり、同じ結果のリークに似ています(死んでいるはずのオブジェクトは存続します)。

また、オブジェクトがピアである場合もあります。おそらく、どちらも同じ3番目のオブジェクトによって所有されているためです。

どちらの方法でも、デリゲートを持つオブジェクトはそのデリゲートを保持するべきではありません。

(ちなみに、少なくとも1つの例外があります。それが何であったか覚えていませんし、それには正当な理由があったとは思いません。)


補遺(2012-05-19で追加):ARCでは、weakではなくを使用する必要がありますassign。弱い参照nilは、オブジェクトが死ぬと自動的に設定され、委任オブジェクトが最終的にデッドデリゲートにメッセージを送信する可能性を排除します。

何らかの理由でARCから離れている場合は、少なくともassignオブジェクトを指すプロパティをに変更しますunsafe_unretained。これにより、これがオブジェクトへの保持されていないがゼロ以外の参照であることを明示的に示します。

assign ARCとMRCの両方で非オブジェクト値に引き続き適切です。


13
NSURLConnectionデリゲートを保持します。

はい、使用しますがweak、それは元の質問の答えにはなりません。なぜAppleのassign代わりに使用するのweakですか?
wcochran 2013年

@wcochran:元の質問は、「なぜ保持assignではなくデリゲートプロパティが与えられるのか」でした。それが尋ねられたときに存在しませんでした。あなたの質問は別のものです、あなたは別に尋ねるべきです。お答えさせていただきます。weak
Peter Hosey 2013年

@wcochranとピーター、その質問はどこかで尋ねられましたか?
ロジャース氏2013

17

割り当てられているデリゲートがある場合、オブジェクトが割り当て解除されるときは常にそのデリゲート値を常にnilに設定することが非常に重要になることに注意してください。そうでない場合、オブジェクトは常にdeallocでデリゲート参照を無視するように注意する必要があります。他の場所でそうしました。


「割り当てられているデリゲートがある場合、オブジェクトが割り当て解除されるときは常に、そのデリゲート値を常にnilに設定することが非常に重要になることに注意してください。」なぜですか?
Peter Hosey、

2
参照が設定されたままになっているため、オブジェクトが割り当て解除された後(予想された種類のオブジェクトに割り当てられていないメモリを指す)は無効になり、使用しようとするとクラッシュします。デバッガーでのこの兆候は、デバッガーが、変数が実際に宣言されているものとは完全に間違っているように見える変数の型を主張する場合です。
Kendall Helmstetter Gelner、

1
これは、委任先のオブジェクトが、タイマーやその他の非同期コールバックなどの別のソースによって保持されている場合にのみ必要です。それ以外の場合は、解放後に割り当てが解除され、デリゲートメソッドを呼び出そうとしません。
Andrew Pouliot 2010

@Andrew:その通りですが、デリゲートをnilにすることを常に実践している場合、それが重要な場合、または誤って保持されたオブジェクトを誤って保持していて、それがとにかく残っている場合は忘れません。デリゲートをnilで除外すると、リークの後にクラッシュが発生するのではなく、単にリークが発生します。
Kendall Helmstetter Gelner、2010

1

その背後にある理由の1つは、保持サイクルを回避することです。AとBの両方がオブジェクトを参照し、メモリから解放されないというシナリオを回避するためです。

実際に割り当ては、NSIntegerやCGFloatなどのプリミティブ型、またはデリゲートなどの直接所有していないオブジェクトに最適です。


それはむしろOPの引用と受け入れられた答えからそれぞれコピーされたものではありませんか?
dakab 2015
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.