2012-04-09 5 views
8

Mam zamiar utworzyć GUI, który będzie dynamicznie tworzył zestawy kontrolek z przypisanymi im zdarzeniami. Będę musiał dodać i usunąć te kontrolki w czasie wykonywania. Będzie to wyglądać tak:Środki ostrożności, które należy podjąć, aby zapobiec wyciekom pamięci z powodu dodanych uchwytów zdarzeń.

FlowLayoutPanel.Controls.Clear(); 
<< add new controls, assigning Click events with += >> 

Słyszałem, że przypisywanie obsługi zdarzeń z + = może spowodować wycieki pamięci (więcej specificly, pamięć nie zostanie zwolniona dopóki aplikacja nie zakończy działania). Chcę tego uniknąć. Wiem, że mogę napisać kilka funkcji, takich jak tutaj How to remove all event handlers from a control, aby znaleźć wszystkie programy do obsługi zdarzeń i je usunąć, ale wygląda to bardzo skomplikowanie.

Czy istnieje inny sposób? Czy wywołanie Dispose help usuwa te procedury obsługi zdarzeń? Czy potrafisz niszczyć obiekty, aby wymusić ich pamięć, tak jak w C/C++?

Dzięki!

PS: Problem polega na tym, że nie wiem, co wydarzenie się rozłączy. Stworzę wiele etykiet i dodaję do nich różne rodzaje zdarzeń onclick. Kiedy nadszedł czas czyszczenia panelu układu przepływu, nie ma możliwości sprawdzenia, który program obsługi zdarzenia został dołączony do której etykiety.

To jest przykładowy kod (_flowLP to FlowLayoutPanel) - ta funkcja Refresh() jest uruchamiana wiele razy przed zakończeniem aplikacji.

private void Refresh() 
    { 
     Label l; 
     Random rnd = new Random(); 

     // What code should i add here to prevent memory leaks 
     _flowLP.Controls.Clear(); 

     l = new Label(); 
     l.Text = "1"; 
     if (rnd.Next(3) == 0) l.Click += Method1; 
     if (rnd.Next(3) == 0) l.Click += Method2; 
     if (rnd.Next(3) == 0) l.Click += Method3; 
     _flowLP.Controls.Add(l); 

     l = new Label(); 
     l.Text = "2"; 
     if (rnd.Next(3) == 0) l.Click += Method1; 
     if (rnd.Next(3) == 0) l.Click += Method2; 
     if (rnd.Next(3) == 0) l.Click += Method3; 
     _flowLP.Controls.Add(l); 

     l = new Label(); 
     l.Text = "3"; 
     if (rnd.Next(3) == 0) l.Click += Method1; 
     if (rnd.Next(3) == 0) l.Click += Method2; 
     if (rnd.Next(3) == 0) l.Click += Method3; 
     _flowLP.Controls.Add(l); 

     l = new Label(); 
     l.Text = "4"; 
     if (rnd.Next(3) == 0) l.Click += Method1; 
     if (rnd.Next(3) == 0) l.Click += Method2; 
     if (rnd.Next(3) == 0) l.Click += Method3; 
     _flowLP.Controls.Add(l); 

     l = new Label(); 
     l.Text = "5"; 
     if (rnd.Next(3) == 0) l.Click += Method1; 
     if (rnd.Next(3) == 0) l.Click += Method2; 
     if (rnd.Next(3) == 0) l.Click += Method3; 
     _flowLP.Controls.Add(l); 

     l = new Label(); 
     l.Text = "6"; 
     if (rnd.Next(3) == 0) l.Click += Method1; 
     if (rnd.Next(3) == 0) l.Click += Method2; 
     if (rnd.Next(3) == 0) l.Click += Method3; 
     _flowLP.Controls.Add(l); 
    } 
+0

Odnośnie do edycji: Jeśli metod dodanych do listy wywołań zdarzenia nie można na pewno określić, można zachować delegatów, które zostały dodane do obsługi zdarzeń w zbiorze gdzieś i użyć go do usunięcia delegatów, gdy czas pochodzi. Lub, jeśli wiesz, że całkowicie usuwasz listy inwokacji zdarzeń, po prostu usuń je całkowicie. – phoog

Odpowiedz

3

to głównie będzie to zmartwienie po podłączeniu krótszy przeżyte zdarzenia do konsumenta dłużej żył producenta zdarzeń. Jeśli mają podobne życie lub są przeciwieństwem tego, co opisałem, to nie problem.

W przypadku, gdy się o to martwisz, po prostu użyj = =, aby odłączyć się od wydarzenia. To usuwa odniesienie utworzone przez załącznik i pomaga uniknąć tego rodzaju problemów z pamięcią.

Edycja: Ponieważ komentarze stają się coraz dłuższe, zamieszczę tutaj kilka uwag. Gdy dołączasz do wydarzenia, wiesz, co robisz, gdy wiesz, kim jest dostawca wydarzeń. Na przykład, jeśli masz klasę Zegara ze zdarzeniem StrikesMidnight i subskrybujesz to wydarzenie z klasy o nazwie Bedtime, rzeczywistą mechaniką Bedtime mówiąc: clock.StrikesMidnight += this.HandleMidnight; jest to, że przypisujesz zegarowi odniesienie do siebie. To tak, jakby Clock miał właściwość obiektu i powiedziałeś, że w przypadku, gdy klasa Bedtime jest krótkotrwała i wykracza poza zakres, Bedtime pojawia się, zawiesza odniesienie do siebie na Clock, a następnie idzie poza zakresem. Problem polega na tym, że zegar nadal ma do niego odniesienie, więc nawet jeśli jest poza zakresem, Garbage Collector nie będzie zbierać czasu na dobranoc.

....

To jest tło. W twoim przypadku tworzysz etykietę i dołączasz do niej odnośnik do niej (za pośrednictwem twoich programów obsługi "MethodX"). Po wywołaniu odświeżania wyczyścisz listę etykiet (co oznacza, że ​​wykraczają poza zakres). Wykraczają one poza zakres i mają odwołania do twojej klasy za pośrednictwem jej obsługi metod MethodX, ale co z tego? Ich posiadanie referencji nie przeszkadza im uzyskać GC. Nikt nie odwołuje się do nich w twoim kodzie, więc GC wykona na nich pracę i nie wycieknie ci pamięć.

+1

Ale nie wiem, co wydarzenie się odłączy! Co wtedy robię? – Istrebitel

+1

To tylko odwrotność tego, co robisz ze swoimi instrukcjami + =. Za każdym razem, gdy dołączasz lokalną funkcję obsługi zdarzeń do zdarzenia, z którym współpracujesz, pamiętaj, aby odłączyć się za pomocą - =, gdy nie musisz już być świadomy zdarzenia, które zostało podniesione. Możesz to zrobić w metodzie dispose(), ale nie jest to absolutnie konieczne. –

+0

tak, ale nie wiem, którą metodę odłączyć. powiedzmy, mam 10 metod, z których jedna została dołączona do 5th of 20 label jakiś czas temu w oparciu o warunki w tym momencie. Teraz muszę usunąć wszystkie etykiety z panelu flowlayout. Skąd mam wiedzieć, który z 10 programów obsługi zdarzeń muszę - =? – Istrebitel

0

Wszystkie elementy sterujące powinny zostać oczyszczone przez pojemnik na śmieci, o ile wyrzuca się formularz zawierający.

Subskrybowanie zdarzenia na elementach sterujących nie spowoduje utrzymania kontroli przy życiu, ponieważ element sterujący ma odniesienie do elementu przekazującego operację; delegat nie ma odniesienia do kontroli.

Sytuacja, w której subskrypcja zdarzenia uniemożliwi wyczyszczenie kontrolki, jest przypadkiem, w którym jakiś kod w formancie subskrybuje zdarzenie poza wystąpieniem formularza, w którym jest zawarty. Na przykład, jeśli niestandardowe pole kombi subskrybuje zdarzenie klasy statycznej, aby kontrolka wiedziała, kiedy należy zaktualizować listę opcji. Jeśli kontrola niestandardowa nie rozprzęgnie tego zdarzenia, będzie się do niego odwoływał rejestr zdarzeń klasy statycznej na czas trwania aplikacji. Ponieważ kontrolka będzie miała odniesienie do swojego kontenera (i tak dalej), cały formularz prawdopodobnie pozostanie w pamięci. W takich przypadkach kontrola powinna rozpakować zdarzenie w jego metodzie Dispose. Subskrybowanie zdarzeń na klasach statycznych lub klasach o długiej żywotności powinno zawsze wywoływać czerwoną flagę i być jawnie niezwiane, gdy nie jest już potrzebne.

W formularzach, o ile wszystkie zdarzenia są połączone z metodami instancji w klasie formularza lub obiektach, których zasięg jest kontrolowany przez instancję formularza, elementy sterujące zostaną oczyszczone, gdy wykres obiektu, w którym znajduje się formularz rootowanie wykracza poza zakres. Tylko uważaj, aby klasy zewnętrzne nie zawierały odniesienia do formularza, gdy nie są już potrzebne.

+0

Innymi słowy, jeśli obiekt wychodzący poza zasięg ma metodę subdomeny innego obiektu do jego zdarzenia, zostanie usunięty i GCed będzie w porządku, ale jeśli obiekt wychodzący poza zakres, niektóre z jego metod zasubskrybowano do innego obiekt zdarzenia, to nie będzie GCed? – Istrebitel

+0

To jest całkiem poprawne. Wciąż będą zbierać śmieci, jeśli obiekt, do którego subskrybują zdarzenia, kwalifikuje się do odebrania. Okrężne odniesienia między obiektami, które w inny sposób można zakwalifikować, nie będą powstrzymywać ich przed gromadzeniem. –

0

Moja pierwsza sugestia to nie wstępna optymalizacja. Upewnij się, że jest to problem, zanim spróbujesz go rozwiązać.

W odniesieniu do Dispose(): utylizacja powinna być TYLKO używana w celu uwolnienia niezarządzanych zasobów, zanim obiekt zostanie zebrany. Aby uzyskać więcej informacji, patrz http://msdn.microsoft.com/en-us/library/system.idisposable.aspx.

Aby uniemożliwić programom obsługi przetwarzanie odwołań do elementów sterujących, należy zrezygnować z subskrypcji wszystkich programów obsługi zdarzeń, które subskrybują podczas tworzenia. WSKAZ :WKA: Jeśli dodajesz programy do obsługi zdarzeń za pośrednictwem interfejsu GUI, zbadaj automatycznie wygenerowany kod, aby zobaczyć, czego potrzebujesz, aby anulować subskrypcję przed wywołaniem FlowLayoutPanel.Controls.Clear(). Czystym (-a?) Sposobem na zrobienie tego byłoby utworzenie własnej kontroli, która dziedziczy po pożądanym sterowaniu i dodaje metodę "Oczyszczania()", która wypisuje się z jakiejkolwiek obsługi zdarzeń, która została zasubskrybowana (powinieneś wiedzieć, które zdarzenie obsługi zostały zasubskrybowane - albo dlatego, że napisałeś kod, albo został on wygenerowany dla ciebie).

+0

Nie, jak powiedziałem, robię to dynamicznie, więc nie przez GUI. – Istrebitel

Powiązane problemy