イベントリスナーは弱い参照で保持されるべきですか?


9

通常、イベントリスナーは、それらを登録したオブジェクトよりも長く存続するべきではありません。

それは、イベントリスナーがデフォルトで弱参照によって保持されるべきであることを意味しますか(オブジェクトリスナーが登録されている弱コレクションに格納されます)?

リスナーがその作成者よりも長生きする必要がある有効なケースはありますか?

それとも、そのような状況は間違いであり、許可されるべきではないのですか?


弱い参照は通常、インスタンスによって表され、これらのインスタンスは、ガベージとして収集する必要があるポイントまで蓄積する可能性もあります。ですから無料のランチではありません。弱い参照を取り除くのと同じロジックで、強い参照を取り除くことができます。
フランクヒルマン2017

回答:


7

なぜイベントリスナーはそれらを登録したオブジェクトよりも長く生きるべきではないのですか?イベントリスナーは、コントロールのメソッド(GUIの例をとる場合)によって登録する必要があると想定しているようです。より正確には、GUIツールキットのコントロールを継承するクラスのオブジェクトによるメソッドです。これは必須ではありません。たとえば、イベントリスナーを登録するために専用のオブジェクトを使用し、後でそのオブジェクトを破棄することができます。

また、イベントリスナーが弱く参照されている場合は、その参照を使用しない場合でも、実際にそれらへの参照を維持する必要があります。そうしないと、リスナーがランダムに収集されます。したがって、次のようなバグが発生します。

  • 誤って作成するのは簡単です(使用することのない参照変数にオブジェクトを格納することを忘れるだけです)。
  • 気づきにくい(GCがそのオブジェクトを収集した場合にのみ、そのバグが発生する)。
  • デバッグが難しい(デバッグセッションでは常にリリースセッションのように機能しますが、GCがオブジェクトを収集した場合にのみ、そのバグが発生します)。

そして、そのバグを回避するだけでは十分なインセンティブにならない場合は、次のようになります。

  1. 作成する各リスナーの名前考える必要があります。

  2. 一部の言語では、静的な分析を使用しており、書き込みも読み取りもされないプライベートメンバーフィールドがある場合に警告を生成します。あなたはそれを上書きするためのメカニズムを使用する必要があります。

  3. イベントリスナーは何かを行い、それが強い参照を持つオブジェクトが収集されると、その何かを行うのをやめます。これで、プログラムの状態に影響を与え、GCに依存する何かができました。つまり、GCはプログラムの具体的な状態に影響を与えます。そして、これは悪いです!

  4. 別のレベルの間接参照があり、参照が収集されたかどうかを確認する必要があるため、弱い参照の処理は遅くなります。弱い参照でイベントリスナーを使用する必要がある場合、これは問題になりませんが、そうではありません。


5

一般に、はい、弱参照を使用する必要があります。ただし、最初に、「イベントリスナー」の意味を明確にする必要があります。

コールバック

一部のプログラミングスタイルでは、特に非同期操作のコンテキストでは、計算の一部を特定のイベントで実行されるコールバックとして表すことが一般的です。たとえば、Promise[ 1 ]にthenは、前のステップの完了時にコールバックを登録するメソッドがあります。

promise =
    Promise.new(async_task)                # - kick off a task
    .then(value => operation_on(value))    # - queue other operations
    .then(value => other_operation(value)) #   that get executed on completion
... # do other stuff in the meanwhile
# later:
result = promise.value # block for the result

ここではthen、promise(イベントソース)がコールバックへの参照を保持する唯一のオブジェクトであるため、によって登録されたコールバックは強力な参照によって保持される必要があります。これは問題ではありません。promise自体の寿命が限られているため、promiseのチェーンが完了した後にガベージコレクションが行われるためです。

オブザーバーパターン

オブザーバーパターンでは、サブジェクトに依存オブザーバーのリストがあります。被験者が何らかの状態になると、オブザーバーは何らかのインターフェースに従って通知されます。オブザーバーを対象に追加したり、対象から削除したりできます。これらのオブザーバーはセマンティックバキュームでは存在しませんが、何らかの目的でイベントを待っています。

この目的がもはや存在しない場合、オブザーバーは対象から削除されます。ガベージコレクションされた言語でも、この削除は手動で実行する必要がある場合があります。オブザーバーの削除に失敗した場合、サブジェクトからオブザーバーへの参照と、オブザーバーが参照するすべてのオブジェクトを介して維持されます。これはメモリを浪費し、(今は役に立たない)オブザーバに通知されるため、パフォーマンスが低下します。

弱い参照は、オブザーバーをガベージコレクションできるようにするため、このメモリリークを修正します。サブジェクトが巡回してすべてのオブザーバーに通知し、オブザーバーへの弱い参照の1つが空であることがわかった場合、その参照は安全に削除できます。あるいは、弱い参照は、サブジェクトが収集時にオブザーバーを削除するクリーンアップコールバックを登録できるように実装される場合があります。

ただし、弱い参照は、オブザーバーの削除を忘れることによって被害を制限するバンドエイドにすぎないことに注意してください。正しい解決策は、不要になったオブザーバーを確実に削除することです。オプションは次のとおりです。

  • 手動で行うとエラーが発生しやすくなります。

  • JavaやusingC#で、リソースと同じようなものを使用してみます。

  • RAIIイディオムなどによる確定的破壊。確定的なガベージコレクションを備えた言語では、デストラクタをトリガーするために、サブジェクトからオブザーバへの弱い参照が必要になる場合があることに注意してください。

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