Déjame poner esto primero y volver a ello:
Una WeakReference es útil cuando desea mantener las pestañas de un objeto, pero NO desea que sus observaciones impidan que se recoja ese objeto
Así que vamos a empezar desde el principio:
--apologías por adelantado para cualquier ofensa no intencional, pero voy a retroceder al nivel de "Dick y Jane" por un momento ya que uno nunca puede decirle a la audiencia.
Entonces, cuando tienes un objeto X
, especifiquémoslo como una instancia de class Foo
, NO PUEDE vivir por sí solo (la mayoría es verdadera); De la misma manera que "Ningún hombre es una isla", solo hay algunas formas en que un objeto puede ser promovido a la Isla, aunque a esto se le llama ser una raíz GC en el lenguaje CLR. Ser una raíz de GC, o tener una cadena establecida de conexiones / referencias a una raíz de GC, es básicamente lo que determina si Foo x = new Foo()
obtiene o no recolección de basura.
Si no puede caminar de regreso a alguna raíz de GC, ya sea por montones o apilados, quedará efectivamente huérfano y probablemente será marcado / recolectado en el próximo ciclo.
En este punto, veamos algunos ejemplos mal construidos:
Primero, nuestro Foo
:
public class Foo
{
private static volatile int _ref = 0;
public event EventHandler FooEvent;
public Foo()
{
_ref++;
Console.WriteLine("I am #{0}", _ref);
}
~Foo()
{
Console.WriteLine("#{0} dying!", _ref--);
}
}
Bastante simple: no es seguro para subprocesos, así que no lo intentes, pero mantiene un "recuento de referencias" aproximado de instancias activas y disminuye cuando se finalizan.
Ahora veamos un FooConsumer
:
public class NastySingleton
{
// Static member status is one way to "get promoted" to a GC root...
private static NastySingleton _instance = new NastySingleton();
public static NastySingleton Instance { get { return _instance;} }
// testing out "Hard references"
private Dictionary<Foo, int> _counter = new Dictionary<Foo,int>();
// testing out "Weak references"
private Dictionary<WeakReference, int> _weakCounter = new Dictionary<WeakReference,int>();
// Creates a strong link to Foo instance
public void ListenToThisFoo(Foo foo)
{
_counter[foo] = 0;
foo.FooEvent += (o, e) => _counter[foo]++;
}
// Creates a weak link to Foo instance
public void ListenToThisFooWeakly(Foo foo)
{
WeakReference fooRef = new WeakReference(foo);
_weakCounter[fooRef] = 0;
foo.FooEvent += (o, e) => _weakCounter[fooRef]++;
}
private void HandleEvent(object sender, EventArgs args, Foo originalfoo)
{
Console.WriteLine("Derp");
}
}
Entonces, tenemos un objeto que ya es una raíz de GC propia (bueno ... para ser específico, estará enraizado a través del dominio de la aplicación que ejecuta esta aplicación, pero ese es otro tema) que tiene dos métodos de enganchar a una instancia Foo
- probémosla:
// Our foo
var f = new Foo();
// Create a "hard reference"
NastySingleton.Instance.ListenToThisFoo(f);
// Ok, we're done with this foo
f = null;
// Force collection of all orphaned objects
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
Ahora, a partir de lo anterior, ¿esperaría que el objeto-that-was-once-referido por f
sea "coleccionable"?
No, porque ahora hay otro objeto que contiene una referencia a él: el Dictionary
en esa instancia estática Singleton
.
Ok, probemos el enfoque débil:
f = new Foo();
NastySingleton.Instance.ListenToThisFooWeakly(f);
// Ok, we're done with this foo
f = null;
// Force collection of all orphaned objects
// This should collect # 2 - you'll see a "#2 dying"
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
Ahora, cuando eliminamos nuestra referencia a- Foo
-th-was-once- f
, no hay más referencias "duras" al objeto, por lo que es coleccionable: el WeakReference
creado por el oyente débil no evitará eso.
Buenos casos de uso:
-
Controladores de eventos (aunque lea esto primero: Eventos débiles en C # )
Tiene una situación en la que causaría una "referencia recursiva" (es decir, el objeto A se refiere al objeto B, que se refiere al objeto A, también conocido como "Pérdida de memoria") (edit: derp, por supuesto, esto no es cierto)
-
Quieres "transmitir" algo a una colección de objetos, pero no quieres ser lo que los mantiene vivos; un List<WeakReference>
se puede mantener fácilmente, e incluso podarse eliminando donde ref.Target == null