Czy procedury obsługi zdarzeń powstrzymują odśmiecanie pamięci?

183

Jeśli mam następujący kod:

MyClass pClass = new MyClass();
pClass.MyEvent += MyFunction;
pClass = null;

Czy pClass będzie zbierany śmieci? Czy też będzie się kręcił, wciąż odpalając wydarzenia, gdy tylko się pojawią? Czy muszę wykonać następujące czynności, aby umożliwić zbieranie śmieci?

MyClass pClass = new MyClass();
pClass.MyEvent += MyFunction;
pClass.MyEvent -= MyFunction;
pClass = null;
Mark Ingram
źródło
11
Wstępnie zasugeruję czytelnikom zainteresowanym tym pytaniem, że warto zapoznać się z lekkimi zdarzeniami / słabymi wzorcami zdarzeń, które NIE zapobiegają gromadzeniu śmieci. Dobry SO bootstrap do tego tematu to stackoverflow.com/questions/185931/…
fostandy
20
Uwaga dla potomności: ustawienie odwołania na wartość null po prostu opóźnia moduł czyszczenia pamięci, rozszerzając o jedną linię zakres odwołania. .NET nie jest VB6.
John Saunders,

Odpowiedzi:

207

W przypadku konkretnego pytania „Czy pClass zostanie wyrzucony”: subskrypcja zdarzenia nie ma wpływu na zbieranie pClass (jako wydawcy).

W przypadku GC ogólnie (w szczególności celu): zależy od tego, czy MyFunction jest statyczna czy oparta na instancji.

Delegat (na przykład subskrypcja zdarzenia) do metody instancji zawiera odwołanie do instancji. Tak więc, subskrypcja wydarzenia zapobiegnie GC. Jednak gdy tylko obiekt publikujący zdarzenie (pClass powyżej) kwalifikuje się do kolekcji, przestaje to stanowić problem.

Zauważ, że jest to jednokierunkowe; tj. jeśli mamy:

publisher.SomeEvent += target.SomeHandler;

wtedy „wydawca” utrzyma „cel” przy życiu, ale „cel” nie utrzyma przy życiu „wydawcy”.

Więc nie: jeśli pClass i tak zostanie zgromadzony, nie ma potrzeby anulowania subskrypcji słuchaczy. Jednakże, jeśli pClass był długowieczny (dłużej niż instancji z MyFunction), następnie pClass mógł utrzymać tę instancję żyje, więc to będzie konieczne, aby wypisać się, jeśli chcesz cel mają być zebrane.

Z tego powodu zdarzenia statyczne są jednak bardzo niebezpieczne, gdy są używane z programami obsługi opartymi na instancjach.

Marc Gravell
źródło
6
Cóż, jeśli pytanie brzmi „czy pClass zostanie wyrzucony”, to odpowiedź „zależy od tego, czy ...” nie jest w rzeczywistości poprawna. To nie zależy od niczego, jak zauważa sam Marc dalej.
Tor Haugen,
@Tor - dość uczciwie - wyjaśnię
Marc Gravell
Chociaż uczestnik subskrypcji wydarzenia wskazuje tylko jeden sposób, subskrybent, który ma zamiar zrezygnować z subskrybowania wydarzenia po jego zakończeniu, potrzebuje jakiejś formy odniesienia do wydawcy. Może to być WeakReference, aw niektórych przypadkach może to być dobry pomysł, ale tak często, jak nie, będzie to silny pomysł.
supercat
Świetna odpowiedź, ponieważ dotyczy także drugiej połowy pytania (które nie zostało zadane): wydawca powstrzyma subskrybenta przed otrzymaniem GC.
Bob Sammers,
Tak, i jak powiedział @BobSammers: Naprawdę może być problem, jeśli instancja o krótkim okresie życia, taka jak Formularz / Okno, subskrybuje usługę o długiej żywotności, taką jak Singleton, która dostarcza dane na przykład: Singleton następnie przechowuje referencję , a przedmioty są przechowywane w pamięci nawet wtedy, gdy uważamy, że są rozładowane! Zachowaj więc ostrożność podczas korzystania z wydarzeń. Nadużywaliśmy wydarzeń związanych z naszym dużym oprogramowaniem i bardzo trudno jest je później rozwiązać.
Elo
9

Tak, pClass będzie śmieciami. Subskrypcja zdarzenia nie oznacza, że ​​istnieje odwołanie do pClass.

Tak więc nie będziesz musiał odłączać modułu obsługi, aby pClass mógł zostać wyrzucony.

Tor Haugen
źródło
8

W momencie, gdy pamięć nie jest już przywoływana, staje się kandydatem do odśmiecania. Kiedy wystąpienie klasy wykracza poza zakres, program nie odwołuje się do niej. Nie jest już używany i dlatego można go bezpiecznie zebrać.

Jeśli nie jesteś pewien, czy coś zostanie zebrane, zadaj sobie następujące pytanie: czy nadal istnieje odniesienie do tego? Do procedur obsługi zdarzeń odwołuje się instancja obiektu, a nie odwrotnie.

lvaneenoo
źródło
0

pClassbędą zbierane śmieci. Jednakże, jeśli kod snippet powyżej znajduje się wewnątrz innej klasy, instancja tej klasy nie mogą być usunięte, jeśli nie zostanie ustawiona pClassna null.

Arav
źródło