Чи зупиняють обробники подій зупиняти збирання сміття?


183

Якщо у мене є такий код:

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

Чи збиратиметься pClass сміття? Або він все ще розпалює свої події, коли вони відбудуться? Чи потрібно мені робити наступне, щоб дозволити збирання сміття?

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

11
Я буду орієнтовно пропонувати читачам, зацікавленим у цьому питанні, що, можливо, варто ознайомитись з легкими подіями / слабкими моделями подій, які НЕ перешкоджають вивезенню сміття. Хороший SO самозавантаження в цю тему stackoverflow.com/questions/185931 / ...
fostandy

20
Примітка для нащадків: встановлення посилання на null просто затримує сміттєзбірник, розширивши на один рядок область посилання. .NET не VB6.
Джон Сондерс

Відповіді:


207

Що стосується конкретного питання "Чи збиратиметься pClass сміття": підписка на подію не впливає на колекцію pClass (як видавця).

Для GC загалом (зокрема, цільової): це залежить від того, чи є MyFunction статичним або на основі екземплярів.

Делегат (наприклад, підписка на подію) методу екземпляра включає посилання на екземпляр. Так, так, передплата на подію запобіжить GC. Однак, як тільки об’єкт, що публікує подію (pClass вище), має право на збір, це перестає бути проблемою.

Зауважте, що це одностороння; тобто якщо у нас є:

publisher.SomeEvent += target.SomeHandler;

тоді "видавець" збереже "ціль" живим, але "ціль" не збереже "видавця" в живих.

Так що ні: якщо pClass все-таки збиратиметься, слухачам не потрібно підписувати підписку. Однак, якщо pClass був довговічним (довше, ніж екземпляр з MyFunction), то pClass міг би зберегти цей екземпляр живим, тому потрібно буде скасувати підписку, якщо ви хочете збирати ціль.

Однак статичні події з цієї причини є дуже небезпечними при використанні з обробниками на основі екземплярів.


6
Ну а якщо питання "чи буде зібрано pClass сміття", то відповідь "залежить від того, чи ..." насправді не правильна. Це не залежить ні від чого, як сам Марк зазначає далі.
Tor Haugen

@Tor - досить справедливо - я
уточню

Хоча делегат підписки на подію вказує лише один спосіб, абоненту, який має намір скасувати підписку на подію, коли буде зроблено з нею, знадобиться певна форма посилання на видавця. Це може бути WeakReference, і в деяких випадках, це може бути хорошою ідеєю, але так часто, як ні, це буде сильним.
supercat

Чудова відповідь, оскільки він також стосується другої половини запитання (цього не задавали): видавець зупинить передплатником не GC'd.
Боб Саммерс

Так, і як сказав @BobSammers, це дійсно може бути проблемою, якщо короткочасний екземпляр, як Form / Window, підписується на службу довгого життя, як Singleton, яка надає дані, наприклад: Singleton зберігає посилання , а предмети зберігаються в пам'яті навіть тоді, коли ми думаємо, що вони вивантажені! Тому будьте дуже обережні, використовуючи події. Ми зловживаємо подіями для нашого великого програмного забезпечення, і це дуже важко вирішити після цього.
Ело

9

Так, pClass буде збирати сміття. Підписка на подію не означає, що існує будь-яка посилання на pClass.

Так ні, вам не доведеться від'єднувати обробник, щоб pClass збирав сміття.


8

У той момент, коли на шматок пам’яті вже не йдеться, він стає кандидатом на вивезення сміття. Коли екземпляр вашого класу виходить за межі, ваша програма більше не посилається на нього. Він більше не використовується і тому може бути безпечно зібраний.

Якщо ви не впевнені, що щось буде зібрано, задайте собі таке питання: чи все ще існує посилання на нього? Обробники подій посилаються на екземпляр об'єкта, а не навпаки.


0

pClassбуде зібрано сміття. Однак якщо фрагмент коду вище знаходиться в іншому класі, екземпляр цього класу може бути не очищений, якщо ви не встановлені pClassна null.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.