Sollten Event-Listener in schwachen Referenzen gehalten werden?

9

Normalerweise sollten Ereignis-Listener Objekte, die sie registriert haben, nicht überleben.

Bedeutet dies, dass Ereignis-Listener standardmäßig von schwachen Referenzen gehalten werden sollten (in schwachen Sammlungen gespeichert, in denen die Objekt-Listener registriert sind)?

Gibt es gültige Fälle, in denen der Hörer seinen Schöpfer überleben sollte?

Oder ist eine solche Situation ein Fehler und sollte nicht erlaubt sein?

mrpyo
quelle
Schwache Referenzen werden normalerweise durch Instanzen dargestellt, und diese Instanzen können sich auch bis zu dem Punkt ansammeln, an dem sie als Müll gesammelt werden müssen. Es ist also kein kostenloses Mittagessen. Dieselbe Logik, die die schwachen Referenzen löscht, könnte starke Referenzen löschen.
Frank Hileman

Antworten:

7

Warum sollten Ereignis-Listener das Objekt, das sie registriert hat, nicht überleben? Es scheint, als würden Sie davon ausgehen, dass Ereignis-Listener nach Steuerungsmethoden registriert werden sollten (wenn wir das GUI-Beispiel verwenden) - genauer gesagt nach Methoden von Objekten von Klassen, die die Steuerelemente des GUI-Toolkits erben. Dies ist keine Notwendigkeit. Sie können beispielsweise ein spezielles Objekt zum Registrieren von Ereignis-Listenern verwenden und dieses Objekt anschließend löschen.

Wenn Ereignis-Listener schwach referenziert wurden, müssten Sie tatsächlich Verweise auf sie behalten, selbst wenn Sie diese Referenz niemals verwenden. Andernfalls wird der Hörer zu einem zufälligen Zeitpunkt gesammelt. Wir bekommen also einen Fehler

  • Einfach aus Versehen zu erstellen (alles, was Sie tun müssen, ist zu vergessen, ein Objekt in einer Referenzvariablen zu speichern, die Sie nie verwenden werden).
  • Schwer zu bemerken (Sie werden diesen Fehler nur bekommen, wenn der GC dieses Objekt sammelt).
  • Schwer zu debuggen (in der Debug-Sitzung - die immer wie eine Release-Sitzung funktioniert - tritt dieser Fehler nur auf, wenn der GC das Objekt erfasst hat).

Und wenn das Vermeiden dieses Fehlers nicht gut genug ist, sind hier einige weitere:

  1. Sie müssen sich für jeden von Ihnen erstellten Listener einen Namen überlegen.

  2. Einige Sprachen verwenden eine statische Analyse, die eine Warnung generiert, wenn Sie ein privates Mitgliedsfeld haben, das niemals geschrieben oder nie gelesen wird. Sie müssen einen Mechanismus verwenden, um dies zu überschreiben.

  3. Der Ereignis-Listener tut etwas, und sobald das Objekt mit seiner starken Referenz gesammelt ist, hört er auf, etwas zu tun. Sie haben jetzt etwas, das den Programmstatus beeinflusst und vom GC abhängt. Dies bedeutet, dass der GC den konkreten Status des Programms beeinflusst. Und das ist schlecht !

  4. Der Umgang mit schwachen Referenzen ist langsamer, da Sie eine andere Indirektionsebene haben und überprüfen müssen, ob die Referenz gesammelt wurde. Dies wäre kein Problem, wenn Event-Listener in schwachen Referenzen erforderlich wären - ist es aber nicht!

Idan Arye
quelle
5

Im Allgemeinen sollten ja schwache Referenzen verwendet werden. Aber zuerst müssen wir uns darüber im Klaren sein, was Sie unter „Event-Listenern“ verstehen.

Rückrufe

In einigen Programmierstilen, insbesondere im Zusammenhang mit asynchronen Operationen, ist es üblich, einen Teil einer Berechnung als Rückruf darzustellen, der bei einem bestimmten Ereignis ausgeführt wird. Zum Beispiel kann eine Promise[ 1 ] eine thenMethode haben, die einen Rückruf nach Abschluss des vorherigen Schritts registriert:

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

Hier müssen die von registrierten Rückrufe thenvon starken Referenzen gehalten werden, da das Versprechen (die Ereignisquelle) das einzige Objekt ist, das eine Referenz auf den Rückruf enthält. Dies ist kein Problem, da das Versprechen selbst eine begrenzte Lebensdauer hat und nach Abschluss der Versprechenskette als Müll gesammelt wird.

Beobachtermuster

Im Beobachtermuster hat ein Subjekt eine Liste abhängiger Beobachter. Wenn das Subjekt in einen Zustand eintritt, werden die Beobachter gemäß einer bestimmten Schnittstelle benachrichtigt. Beobachter können dem Motiv hinzugefügt und daraus entfernt werden. Diese Beobachter existieren nicht in einem semantischen Vakuum, sondern warten auf Ereignisse zu einem bestimmten Zweck.

Wenn dieser Zweck nicht mehr besteht, sollten die Beobachter aus dem Subjekt entfernt werden. Selbst in durch Müll gesammelten Sprachen muss diese Entfernung möglicherweise manuell durchgeführt werden. Wenn wir einen Beobachter nicht entfernen, wird er über die Referenz des Subjekts zum Beobachter und damit alle Objekte, auf die der Beobachter verweist, am Leben erhalten. Dies verschwendet Speicher und beeinträchtigt die Leistung, da der (jetzt unbrauchbare) Beobachter weiterhin benachrichtigt wird.

Schwache Referenzen beheben dieses Speicherleck, da sie es dem Beobachter ermöglichen, Müll zu sammeln. Wenn das Subjekt herumgeht, um alle Beobachter zu benachrichtigen, und feststellt, dass einer der schwachen Verweise auf einen Beobachter leer ist, kann dieser Verweis sicher entfernt werden. Alternativ könnten die schwachen Referenzen so implementiert werden, dass der Betreff einen Bereinigungsrückruf registrieren kann, der den Beobachter beim Sammeln entfernt.

Beachten Sie jedoch, dass schwache Referenzen nur ein Pflaster sind, das den Schaden begrenzt, indem es vergisst, einen Beobachter zu entfernen. Die richtige Lösung wäre, sicherzustellen, dass ein Beobachter entfernt wird, wenn er nicht mehr benötigt wird. Zu den Optionen gehören:

  • Manuell machen, aber das ist fehleranfällig.

  • Verwenden Sie etwas Ähnliches wie das Ausprobieren mit Ressourcen in Java oder usingin C #.

  • Deterministische Zerstörung, etwa über das RAII-Idiom. Beachten Sie, dass in einer Sprache mit deterministischer Speicherbereinigung möglicherweise noch schwache Verweise des Subjekts auf den Beobachter erforderlich sind, um den Destruktor auszulösen.

amon
quelle