Iteam
GitHub

Se upp med eventhanterare som pekar på lagrade objekt

En intressant liten artikel (egentligen förtäckt reklam för en produkt, men låt gå) om ett gäng som skrivit kod i C# för att styra en förarlös bil i The Darpa Challenge: http://www.codeproject.com/showcase/IfOnlyWedUsedANTSProfiler.asp

Det här är samma sak som vi själva upptäckte för något år sedan (men aldrig bloggade om): om objekt1 lyssnar på events från objekt2 kan objekt1 inte tas bort av garbage collectorn förrän objekt2 också tas bort - eftersom de har en stark koppling. Ett fall där detta kan inträffa i ASP.NET är om man har objekt i Session som genererar events, som man låter webbkontroller lyssna på. Då kommer webbkontrollen att ligga kvar i minnet även efter att sidan laddats ur (då de normalt städas bort).

En fin lösning på det här problemet är att använda svaga referenser, som t.ex. finns inbyggt i Smalltalk: http://www.haible.de/bruno/papers/cs/weak/WeakDatastructures-writeup.html.
.NET har en WeakReference-klass, http://msdn2.microsoft.com/en-us/library/system.weakreference.aspx, men jag har aldrig sett den användas någonstans. Framförallt inte på events.

Så det rätta att göra i .NET för att undvika det här är att alltid se till att städa upp sina event-listeners. Det som gör det lite ointuitivt är att i alla exempel från Microsoft så sparas aldrig någon referens till eventlyssnarna, så det går inte att ta bort dem. Så här brukar det se ut:

foo.AnEvent += new EventHandler(AMethod);

dvs. man skapar upp ett EventHandler-objekt som man sen kopplar men inte har någon annan referens till. På så sätt så har man sen ingen möjlighet att ta bort kopplingen. I 99% av fallen vad gäller t.ex. webbsidor så gör det inget - allt tas ändå bort av sig självt när webbförfrågan är över. Men om man nu vill ta bort lyssnaren på ett fint sätt så måste man istället göra såhär:

EventHandler anEventHandler = new EventHandler(AMethod);
foo.AnEvent += anEventHandler;
/* .... massor av kod etc... */
foo.AnEvent -= anEventHandler;

När man gör foo.AnEvent -= anEventHandler så tar man bort referensen till this ifrån foo, vilket gör att this inte har några starka referenser och kan städas bort ur minnet som vanligt.

Erik Hjortsberg
2007-11-19