I riferimenti deboli sono importanti nel contesto di uno schema di gestione della memoria con conteggio dei riferimenti. Ad esempio, potremmo avere una struttura dati ad albero in cui ciascun nodo conosce il nodo genitore. Il risultato è una struttura di dati circolare. Quando abbandoniamo tutti i riferimenti noti alla radice dell'albero, l'albero non viene liberato perché i riferimenti dai nodi figlio ai nodi radice impediscono al refcount di cadere a zero.
Questo può essere evitato con riferimenti deboli. Quando ogni riferimento da un figlio al suo genitore è un riferimento debole (un riferimento che non influenza il conteggio dei riferimenti), quindi un riferimento da una delle nostre variabili alla radice dell'albero è il conteggio solo riferimento. Se rimuoviamo quel riferimento, il Refcount raggiunge zero (o no, se sono stati fatti altri riferimenti) e la memoria può essere recuperata.
L'implicazione qui è che finché siamo interessati ai nodi dell'albero, manterremo anche la radice dell'albero stesso. Pertanto, i riferimenti deboli a un nodo genitore rimarranno sempre validi.
Un esempio interessante di un albero con tali proprietà è il Document Object Model per i documenti XML o HTML. Un nodo nel DOM non può esistere separatamente dal documento (o frammento del documento) a cui appartiene. Il DOM contiene accessors che consentono a un'implementazione di mantenere l'integrità referenziale anche se implementata con riferimenti o puntatori deboli.
Ci sono alcune osservazioni importanti.
-
I riferimenti deboli sono in gran parte non necessari con schemi di gestione della memoria più sofisticati come la garbage collection. Ad esempio, potremmo interrompere l'esecuzione di un programma e nel grafico di tutti i riferimenti trovare tutti i sottografi scollegati. Tutti questi sottografi scollegati tranne quello principale possono essere raccolti (anche se l'implementazione della semantica della distruzione può essere difficile, inoltre, questo rende l'idioma RAII impossibile o difficile).
Come sottolineato nei commenti, alcune implementazioni di notificatori possono trarre grandi benefici dai riferimenti deboli (azzerati) anche nel contesto delle lingue di GC.
-
I riferimenti deboli sono fondamentalmente equivalenti ai normali puntatori. Devi strutturare il tuo programma in modo da garantire che punti sempre a qualcosa di valido. Anche se a volte difficile, l'esperienza ha dimostrato che questo non è esattamente impossibile (ad esempio mantenendo il contesto intorno).
-
Un'implementazione che imposta riferimenti deboli a null
quando l'oggetto di riferimento viene liberato può essere altamente inefficiente, poiché abbiamo bisogno di un riferimento dall'oggetto di riferimento per tornare al riferimento stesso, in modo che possa essere ripristinato alla liberazione.
L'altra opzione sarebbe accedere a ciascun oggetto solo attraverso un proxy, dove i riferimenti deboli aumentano solo il numero di proxy e i riferimenti normali aumentano sia il numero di proxy che l'oggetto reale. Quando il conteggio dell'oggetto interno è zero, il proxy deve essere informato della distruzione per impostare il suo puntatore interno su null
. Ciò implica che un riferimento debole di questo design richiede una profonda integrazione con la gestione della memoria del runtime e non può essere aggiunto in seguito come libreria. Questo è più efficiente in termini di memoria, a scapito dell'aggiunta di un livello di puntatore aggiuntivo a ciascun accesso.