Les auditeurs d'événements devraient-ils être tenus dans des références faibles?

9

Habituellement, les écouteurs d'événements ne doivent pas survivre à l'objet qui les a enregistrés.

Cela signifie-t-il que les écouteurs d'événements doivent être détenus par des références faibles par défaut (stockées dans des collections faibles par les écouteurs d'objets enregistrés)?

Existe-t-il des cas valides où l'auditeur devrait survivre à son créateur?

Ou peut-être qu'une telle situation est une erreur et qu'elle ne devrait pas être autorisée?

mrpyo
la source
Les références faibles sont généralement représentées par des instances, et ces instances peuvent également s'accumuler au point où elles doivent être collectées en tant qu'ordures. Ce n'est donc pas un déjeuner gratuit. La même logique qui efface les références faibles pourrait effacer les références fortes.
Frank Hileman

Réponses:

7

Pourquoi les écouteurs d'événements ne devraient-ils pas survivre à l'objet qui les a enregistrés? Il semble que vous supposiez que les écouteurs d'événements devraient être enregistrés par des méthodes de contrôles (si nous prenons l'exemple de l'interface graphique) - ou plus précisément, des méthodes par des objets de classes qui héritent des contrôles de la boîte à outils de l'interface graphique. Ce n'est pas une nécessité - vous pouvez, par exemple, utiliser un objet spécialisé pour enregistrer les écouteurs d'événements et abandonner cet objet par la suite.

De plus, si les écouteurs d'événements sont faiblement référés, vous devrez en fait conserver des références même si vous n'utilisez jamais cette référence. Dans le cas contraire, l'auditeur sera collecté à un moment aléatoire. Nous obtenons donc un bug qui est

  • Facile à créer par erreur (tout ce que vous avez à faire est d'oublier de stocker un objet dans une variable de référence que vous n'utiliserez jamais).
  • Difficile à remarquer (vous n'aurez ce bug que si le GC récupère cet objet).
  • Difficile à déboguer (dans la session de débogage - qui fonctionne toujours comme une session de publication - vous ne rencontrerez ce bogue que si le GC a collecté l'objet).

Et si éviter ce bug n'est pas une incitation suffisante, voici quelques autres:

  1. Vous devrez penser à un nom pour chaque auditeur que vous créez.

  2. Certaines langues utilisent une analyse statique qui génère un avertissement si vous avez un champ membre privé qui n'est jamais écrit ou jamais lu. Vous devrez utiliser un mécanisme pour remplacer cela.

  3. L'auditeur d'événements fait quelque chose, et une fois que l'objet qui a sa référence forte est collecté, il cessera de faire ce quelque chose. Vous avez maintenant quelque chose qui affecte l'état du programme et dépend du GC - ce qui signifie que le GC affecte l'état concret du programme. Et c'est MAUVAIS !

  4. La gestion des références faibles est plus lente, car vous avez un autre niveau d'indirection et puisque vous devez vérifier si la référence a été collectée. Ce ne serait pas un problème si avoir des écouteurs d'événements dans des références faibles était nécessaire - mais ce n'est pas le cas!

Idan Arye
la source
5

En général, oui, des références faibles doivent être utilisées. Mais d'abord, nous devons être clairs sur ce que vous entendez par «auditeurs d'événements».

Rappels

Dans certains styles de programmation, en particulier dans le contexte des opérations asynchrones, il est courant de représenter une partie d'un calcul comme un rappel qui est exécuté sur un certain événement. Par exemple, un Promise[ 1 ] peut avoir une thenméthode qui enregistre un rappel à la fin de l'étape précédente:

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

Ici, les rappels enregistrés par thendoivent être tenus par des références fortes, car la promesse (la source de l'événement) est le seul objet contenant une référence au rappel. Ce n'est pas un problème car la promesse elle-même a une durée de vie limitée et sera récupérée après la fin de la chaîne de promesses.

Modèle d'observateur

Dans le modèle d'observateur, un sujet a une liste d'observateurs dépendants. Lorsque le sujet entre dans un état, les observateurs sont avertis selon une certaine interface. Des observateurs peuvent être ajoutés et supprimés du sujet. Ces observateurs n'existent pas dans un vide sémantique, mais attendent des événements dans un certain but.

Si cet objectif n'existe plus, les observateurs doivent être retirés du sujet. Même dans les langues récupérées, cette suppression peut devoir être effectuée manuellement. Si nous ne parvenons pas à retirer un observateur, il sera maintenu en vie via la référence du sujet à l'observateur, et avec lui tous les objets auxquels l'observateur fait référence. Cela gaspille de la mémoire et dégrade les performances car l'observateur (désormais inutile) sera toujours averti.

Les références faibles corrigent cette fuite de mémoire, car elles permettent à l'observateur d'être récupéré. Lorsque le sujet se déplace pour avertir tous les observateurs et constate qu'une des références faibles à un observateur est vide, cette référence peut être supprimée en toute sécurité. Alternativement, les références faibles peuvent être implémentées de manière à permettre au sujet d'enregistrer un rappel de nettoyage qui supprimera l'observateur lors de la collecte.

Mais notez que les références faibles ne sont qu'un pansement qui limite les dégâts en oubliant de retirer un observateur. La bonne solution serait de s'assurer qu'un observateur est retiré lorsqu'il n'est plus nécessaire. Les options comprennent:

  • Le faire manuellement, mais cela est sujet à des erreurs.

  • Utiliser quelque chose qui ressemble à essayer avec des ressources en Java ou usingen C #.

  • Destruction déterministe, par exemple via l'idiome RAII. Notez que dans un langage avec un ramasse-miettes déterministe, cela pourrait encore nécessiter de faibles références du sujet à l'observateur afin de déclencher le destructeur.

amon
la source