In generale, sì, i riferimenti deboli dovrebbero essere usati. Ma prima dobbiamo essere chiari su cosa intendi per "ascoltatori di eventi".
richiamate
In alcuni stili di programmazione, specialmente nel contesto di operazioni asincrone, è comune rappresentare una parte di un calcolo come un callback che viene eseguito su un determinato evento. Ad esempio un Promise
[ 1 ] può avere un metodo then
che registra una richiamata al completamento del passaggio precedente:
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
Qui, i callback registrati da then
devono essere tenuti da riferimenti forti, in quanto la promessa (la fonte dell'evento) è l'unico oggetto che contiene un riferimento al callback. Questo non è un problema in quanto la promessa stessa ha una durata limitata e sarà raccolta dopo la catena di promesse.
Pattern osservatore
Nel modello di osservatore, un soggetto ha una lista di osservatori dipendenti. Quando il soggetto entra in uno stato, gli osservatori vengono avvisati secondo alcune interfacce. Gli osservatori possono essere aggiunti e rimossi dal soggetto. Questi osservatori non esistono in un vuoto semantico, ma sono in attesa di eventi per qualche scopo.
Se questo scopo non esiste più, gli osservatori dovrebbero essere rimossi dal soggetto. Anche nei linguaggi raccolti con garbage, questa rimozione potrebbe dover essere eseguita manualmente. Se non riusciamo a rimuovere un osservatore, sarà tenuto in vita tramite il riferimento dal soggetto all'osservatore e con esso tutti gli oggetti a cui fa riferimento l'osservatore. Questo spreca memoria e degrada le prestazioni man mano che l'osservatore (ora inutile) verrà comunque avvisato.
I riferimenti deboli correggono questa perdita di memoria, in quanto consentono all'osservatore di essere raccolto. Quando il soggetto va in giro per notificare a tutti gli osservatori e scopre che uno dei riferimenti deboli a un osservatore è vuoto, tale riferimento può essere rimosso in modo sicuro. In alternativa, i riferimenti deboli potrebbero essere implementati in un modo che consenta al soggetto di registrare un callback di pulizia che rimuoverà l'osservatore al momento della raccolta.
Ma nota che i riferimenti deboli sono solo un cerotto che limita il danno dimenticando di rimuovere un osservatore. La soluzione corretta sarebbe quella di assicurarsi che un osservatore venga rimosso quando non è più necessario. Le opzioni includono:
-
Eseguendolo manualmente, ma è soggetto a errori.
-
Usare qualcosa di simile a try-with-resource in Java o using
in C #.
-
Distruzione deterministica, ad esempio tramite l'idioma RAII. Si noti che in un linguaggio con garbage collection deterministico, questo potrebbe richiedere ancora riferimenti deboli dall'oggetto all'osservatore per attivare il distruttore.