Подтвердить что ты не робот

Нужно ли мне удалять подписки на события с объектов до их сиротства?

Если у моего программного обеспечения есть два экземпляра объекта, один из которых подписан на события другого. Нужно ли мне отписывать их друг от друга, пока они не осиротели, чтобы их очистили сборщик мусора? Или есть ли другая причина, по которой я должен очищать отношения событий? Что делать, если подписанный объект является сиротой, но абонент не является, или наоборот?

4b9b3361

Ответ 1

Да, да. Издатели событий держат ссылки на объекты и не мешают им собирать мусор.

Посмотрите пример, чтобы узнать, что произойдет. У нас есть два класса; один раскрывает событие, другой потребляет его:

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
    }
}

Обратите внимание, что ClassB не хранит ссылку на экземпляр ClassA; он просто подключает обработчик событий.

Теперь посмотрим, как собираются объекты. Сценарий 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();

Мы создаем экземпляр ClassB и сохраняем ссылку на него через временную переменную. Он получает новый экземпляр ClassA, где мы не храним ссылку на него в любом месте, поэтому он выходит за пределы области сразу после завершения конструктора ClassB. У нас есть сборщик мусора, который запускается один раз, когда ClassA вышел из сферы действия, и однажды, когда ClassB вышел из сферы действия. Выход:

Collect 1
A being collected
Collect 2
B being collected

Сценарий 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();

Создается новый экземпляр класса ClassA, и ссылка на него сохраняется в переменной temp. Затем создается новый экземпляр ClassB, который получает экземпляр ClassA в temp, переданный ему, и мы храним ссылку на него в temp2. Затем мы устанавливаем temp2 равным null, делая экземпляр ClassB выходящим из области видимости. Как и раньше, мы запускаем сборщик мусора после того, как каждый экземпляр вышел из сферы действия. Выход:

Collect 1
Collect 2
B being collected
A being collected

Итак, в заключение; если экземпляр, который выдает событие, выходит за рамки, он становится доступным для сбора мусора, независимо от того, подключены ли обработчики событий или нет. Если экземпляр с обработчиком событий подключен к событию в другом экземпляре, он не будет доступен для сбора мусора до тех пор, пока обработчик события не будет удален или экземпляр, к которому прикреплен обработчик события, станет доступен для сбора мусора.

Ответ 2

Вам нужно только отменить события, если объект, подверженный событиям, является долговечным, но объект, связавший событие, в противном случае был бы недолговечным (и получить сбор мусора довольно быстро).

В этом случае отказ от отсоединения приведет к утечке памяти из-за того, что ваш недолговечный объект не сможет быть GCed - поскольку событие в долгоживущем объекте удерживается на делегате, который выполняется ссылка на короткоживущий объект. Так как объект, на который все еще ссылается этот делегат, не может получить сбор мусора.

Статические события долговечны по определению - они живут до выхода программы. Если вы подключите статическое событие, вы обязательно должны отцепить его, когда закончите.

Если оба объекта будут потеряны, отсоединение не требуется.

Ответ 3

Подписывание события приводит к сильной ссылке на подписчика. Это связано с тем, что в рамках обложки события являются делегатами, а делегаты к методам экземпляра являются комбинацией ссылки на объект и фактического метода. Если вы не отмените подписку, издатель продолжит поддерживать ссылки, а подписывающиеся объекты никогда не станут по-настоящему сиротами (и GC'ed) до тех пор, пока издатель жив.

Обратное неверно, то есть подписанный объект не имеет ссылки на издателя.