2009-07-02 15 views
21

Jeśli moje oprogramowanie zawiera dwie instancje obiektów, z których jedna jest subskrybowana do zdarzeń drugiej. Czy muszę zrezygnować z ich subskrypcji, zanim zostaną osierocone, aby zostały oczyszczone przez śmieciarza? Czy jest jakiś inny powód, dla którego powinienem wyczyścić relacje wydarzeń? Co się stanie, jeśli subskrybowany obiekt zostanie osierocony, ale subskrybent nie jest lub vice versa?Czy muszę usuwać subskrypcje zdarzeń z obiektów, zanim zostaną osierocone?

Odpowiedz

22

Tak, robisz. Wydawcy wydarzeń przechowują odniesienia do obiektów i zapobiegają ich zbieraniu śmieci.

Spójrzmy na przykład, aby zobaczyć, co się stanie. Mamy dwie klasy; jeden naraża zdarzenie, drugi zużywa go:

class ClassA 
{ 
    public event EventHandler Test; 
    ~ClassA() 
    { 
     Console.WriteLine("A being collected"); 
    } 
} 
class ClassB 
{ 
    public ClassB(ClassA instance) 
    { 
     instance.Test += new EventHandler(instance_Test); 
    } 

    ~ClassB() 
    { 
     Console.WriteLine("B being collected"); 
    } 

    void instance_Test(object sender, EventArgs e) 
    { 
     // this space is intentionally left blank 
    } 
} 

Uwaga jak ClassB nie przechowuje referencję do instancji ClassA; po prostu podpina program obsługi zdarzeń.

Zobaczmy teraz, jak są zbierane obiekty. Scenariusz 1:

ClassB temp = new ClassB(new ClassA()); 
Console.WriteLine("Collect 1"); 
GC.Collect(); 
Console.ReadKey(); 
temp = null; 
Console.WriteLine("Collect 2"); 
GC.Collect(); 
Console.ReadKey(); 

Tworzymy instancję klasy B i przechowujemy do niej odwołanie za pomocą zmiennej tymczasowej. Otrzymuje nową instancję klasy ClassA, gdzie nie przechowujemy odnośnika do niej w dowolnym miejscu, więc wykracza poza zakres natychmiast po zakończeniu konstruktora klasy B. Mamy moduł do zbierania śmieci uruchamiany raz, gdy klasa A wykracza poza zakres, a raz, gdy klasa B wykracza poza zakres. Wyjście:

Collect 1 
A being collected 
Collect 2 
B being collected 

Scenariusz 2

ClassA temp = new ClassA(); 
ClassB temp2 = new ClassB(temp); 
temp2 = null; 
Console.WriteLine("Collect 1"); 
GC.Collect(); 
Console.ReadKey(); 
temp = null; 
Console.WriteLine("Collect 2"); 
GC.Collect(); 
Console.ReadKey(); 

Nowy przykład ClassA jest tworzony i odnieść się do nich jest przechowywany w zmiennej temp. Następnie tworzone jest nowe wystąpienie klasy B, co powoduje przekazanie instancji ClassA w temacie, a my przechowujemy odniesienie do niej w temp2. Następnie ustawiamy temp2 na wartość null, powodując, że instancja klasy B wychodzi poza zasięg. Tak jak poprzednio, uruchamiamy garbage collector po tym, jak każda instancja wykracza poza zakres. Na wyjściu:

Collect 1 
Collect 2 
B being collected 
A being collected 

Podsumowując; jeśli instancja udostępniająca zdarzenie wykracza poza zakres, staje się dostępna dla funkcji czyszczenia pamięci, bez względu na to, czy są dostępne procedury obsługi zdarzeń, czy nie. Jeśli instancja, która ma przypisaną obsługę zdarzeń do zdarzenia w innej instancji, nie będzie dostępna dla czyszczenia pamięci, dopóki nie zostanie odłączona obsługa zdarzeń lub instancja, do której dołączony jest moduł obsługi zdarzeń, stanie się dostępna do czyszczenia pamięci.

9

Wystarczy odpiąć zdarzenia jeśli obiekt narażając wydarzenia jest długowieczny, ale obiekt zaczepiając zdarzenie inaczej byłby krótkotrwały (i trafiają śmieci zebrano dość szybko).

W takim przypadku brak odłączenia spowoduje, że sprowadza się to do wycieku pamięci, ponieważ obiekt krótkotrwały nie będzie mógł zostać GCed - ponieważ zdarzenie w obiekcie długożyciowym utrzymuje się na delegacie, który zawiera odniesienie do obiektu krótkotrwałego. Ponieważ ten krótkotrwały obiekt jest wciąż przywoływany przez tego delegata, nie może uzyskać zbierania śmieci.

Zjawiska statyczne są długowieczne z definicji - trwają do momentu zakończenia programu. Jeśli podejmiesz statyczne wydarzenie, zdecydowanie powinieneś go odczepić, kiedy skończysz.

Jeśli oba obiekty mają zostać osierocone, odhaczenie nie jest konieczne.

5

Subskrybowanie zdarzenia powoduje silne nawiązanie do subskrybenta. Dzieje się tak dlatego, że pod osłonami zdarzenia są delegatami, a delegaty do metod instancji są kombinacją odwołania do obiektu i faktycznej metody. Jeśli nie zrezygnujesz z subskrypcji, wydawca będzie nadal utrzymywał referencje, a subskrybowane obiekty nigdy nie zostaną prawdziwie osierocone (i GC), dopóki wydawca żyje.

Odwrotna sytuacja nie jest prawdą, tj. Zasubskrybowany obiekt nie ma żadnego odniesienia do wydawcy.

Powiązane problemy