Bu karışıklığı https://www.spicelogic.com/Blog/net-event-handler-memory-leak-16 adresindeki bir blogda açıkladım . Açık bir fikre sahip olabilmek için burada özetlemeye çalışacağım.
Referans, "İhtiyaç" anlamına gelir:
Her şeyden önce, eğer A nesnesi B nesnesine bir başvuru içeriyorsa, bunun A nesnesinin çalışması için B nesnesine ihtiyaç duyduğunu anlamalısınız. Bu nedenle, çöp toplayıcı A nesnesi bellekte olduğu sürece B nesnesini toplamaz.
Bence bu bölüm bir geliştirici için açık olmalı.
+ = Sağ taraftaki nesnenin sol nesneye referansını enjekte etmek anlamına gelir:
Ancak, karışıklık C # + = operatöründen gelir. Bu operatör geliştiriciye, bu operatörün sağ tarafının aslında sol taraftaki nesneye bir referans enjekte ettiğini açıkça söylemez.
Ve bunu yaparak, A nesnesinin B nesnesine ihtiyacı olduğunu düşünür, ancak bakış açınızdan, A nesnesinin B nesnesinin yaşayıp yaşamadığını umursamaması gerekir. A nesnesi B nesnesinin gerekli olduğunu düşündüğü için A nesnesi, A nesnesi canlı olduğu sürece B nesnesini çöp toplayıcıdan korur. Ancak, olay abone nesnesine bu korumanın verilmesini istemediyseniz, bir bellek sızıntısı meydana geldiğini söyleyebilirsiniz.
Olay gidericiyi ayırarak böyle bir sızıntıyı önleyebilirsiniz.
Nasıl karar verilir?
Ancak, kod tabanınızın tamamında çok sayıda olay ve olay işleyicisi vardır. Bu, olay işleyicilerini her yerde ayırmaya devam etmeniz gerektiği anlamına mı geliyor? Cevap hayır. Bunu yapmak zorunda olsaydınız, kod tabanınız ayrıntılı bir şekilde çirkin olacaktır.
Bir ayırma olayı işleyicisinin gerekli olup olmadığını belirlemek için basit bir akış şemasını takip edebilirsiniz.
Çoğu zaman, olay abone nesnesinin olay yayıncı nesnesi kadar önemli olduğunu ve her ikisinin de aynı anda yaşadığı düşünülebilir.
Endişelenmenize gerek olmayan bir senaryo örneği
Örneğin, bir pencerenin düğme tıklama olayı.
Burada, olay yayıncısı Düğme ve olay abonesi MainWindow'dur. Bu akış şemasını uygulayarak bir soru sorun, Ana Pencerenin (olay abonesi) Düğme'den (olay yayıncısı) önce ölmüş olması gerekiyor mu? Açıkçası Hayır. Bu hiç mantıklı değil. Öyleyse, tıklama etkinliği işleyicisini ayırma konusunda neden endişeleniyorsunuz?
Bir olay işleyicisi ayrılması GEREKİR olduğunda bir örnek.
Abone nesnesinin yayıncı nesnesinden önce ölmüş olması gereken bir örnek vereceğim. Diyelim ki MainWindow'unuz "SomethingHappened" adlı bir olay yayınlıyor ve ana pencereden bir düğmeyi tıklatarak bir alt pencere görüntülüyorsunuz. Alt pencere, ana pencerenin o olayına abone olur.
Ve alt pencere, Ana Pencerenin bir olayına abone olur.
Bu koddan, Ana Pencerede bir düğme olduğunu açıkça anlayabiliriz. Bu düğmeye tıklandığında bir Çocuk Penceresi gösterilir. Alt pencere, ana pencereden bir olay dinler. Bir şey yaptıktan sonra kullanıcı alt pencereyi kapatır.
Şimdi, akış şemasına göre, "Alt pencere (olay abonesi) olay yayıncısından (ana pencere) önce ölmüş olmalı mı?" Sorusunu sorarsanız, yanıt EVET olmalıdır. Doğru mu? Bunu genellikle Pencerenin Yüksüz olayından yaparım.
Temel kural: Görünümünüz (örn. WPF, WinForm, UWP, Xamarin Formu vb.) Bir ViewModel olayına abone olursa, her zaman olay işleyiciyi ayırmayı unutmayın. Çünkü bir ViewModel genellikle bir görünümden daha uzun yaşar. Bu nedenle, ViewModel imha edilmezse, o ViewModel'e abone olan herhangi bir görünüm bellekte kalır ve bu iyi değildir.
Bir bellek profiler kullanarak kavramın kanıtı.
Bir bellek profili oluşturucu ile konsepti doğrulayamazsak çok eğlenceli olmaz. Bu deneyde JetBrain dotMemory profiler kullandım.
İlk olarak, MainWindow, bu şekilde ortaya çıktı:
Sonra bir anı fotoğrafı çektim. Sonra düğmeyi 3 kez tıkladım . Üç çocuk penceresi geldi. Tüm bu alt pencereleri kapattım ve Çöp Toplayıcı'nın çağrıldığından emin olmak için dotMemory profilindeki GC'yi Zorla düğmesini tıklattım. Sonra başka bir bellek fotoğrafı aldım ve karşılaştırdım. Seyretmek! korkumuz doğruydu. Çocuk Penceresi, kapatıldıktan sonra bile Çöp toplayıcı tarafından toplanmadı. Sadece bu değil, ChildWindow nesnesi için sızan nesne sayısı da " 3 " olarak gösterilir (3 alt pencereyi göstermek için düğmeye 3 kez tıkladım).
Tamam, sonra olay işleyicisini aşağıda gösterildiği gibi ayırdım.
Sonra aynı adımları uyguladım ve bellek profilini kontrol ettim. Bu sefer, vay! daha fazla bellek sızıntısı yok.