我使用弱事件管理器(如PropertyChangedEventManager
)來訂閱我的WPF應用程序中的事件。WPF中「徘徊」的弱事件 - 這是預期的行爲嗎?
但是,我發現了一個可能的內存泄漏 - 但我認爲它可能不是嚴格的內存泄漏,只是在垃圾收集器撿起它們之前對事件做出反應。這裏有一個例子展示我的問題:
的MainWindow
類和可愛的隱藏代碼與屬性號碼,我要聽,具有較強的參考監聽類。
它有兩個按鈕事件,一個添加/刪除強引用,一個改變的屬性號:
public partial class MainWindow : Window, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
// strong reference to listening class that will be added/removed
private TestReferenceClass strongReference;
// property that the listening class will listen to
private int number;
public int Number
{
get { return number; }
set { number = value; NotifyPropertyChanged(); }
}
// for UI
private bool referenceExists;
public bool ReferenceExists
{
get { return referenceExists; }
set { referenceExists = value; NotifyPropertyChanged(); }
}
//button event to change Number
private void ChangeProp(object sender, RoutedEventArgs e)
{
Number++;
}
//button event to assign/remove reference
private void ToggleReference(object sender, RoutedEventArgs e)
{
if (strongReference != null)
{
strongReference = null;
ReferenceExists = false;
}
else
{
strongReference = new TestReferenceClass(this);
ReferenceExists = true;
}
}
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
隨着XAML:
<Window x:Class="WpfApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="MainWindow" Height="150" Width="525">
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
</Window.Resources>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" Orientation="Horizontal">
<Button Margin="10, 0" Click="ToggleReference">Toggle Reff</Button>
<Grid>
<TextBlock Text="No Refference" Background="White" VerticalAlignment="Center"/>
<TextBlock Text="Refference" Visibility="{Binding ReferenceExists, Converter={StaticResource BooleanToVisibilityConverter}}" Background="White" VerticalAlignment="Center"/>
</Grid>
<Button Margin="10, 0" Click="ChangeProp">Change property</Button>
<TextBox Text="{Binding Number}" Width="200" IsReadOnly="True"/>
</StackPanel>
</Window>
然後是「引用」監聽屬性更改的類。這只不過是創建和使用弱事件偵聽器PropertyChangedEventManager
有關數字的變化監聽:
public class TestReferenceClass
{
public TestReferenceClass(MainWindow source)
{
// add a weak event manager
PropertyChangedEventManager.AddHandler(source, Source_PropertyChanged, string.Empty);
}
private void Source_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "Number")
{
Console.WriteLine("Property changed! " + (sender as MainWindow).Number);
}
}
}
結果是一個小的應用程序,可以讓你創建和銷燬參考,並引發他們的活動,只留下弱聽者保持參照:
變得與它亂搞了一下,你可以觸發唯一正在舉行由弱引用的對象上的事件後明確。如果你幾次垃圾郵件的創建/銷燬按鈕就可以觸發多個事件爲相同的點擊這樣的:
Property changed! 3
Property changed! 3
Property changed! 3
Property changed! 4
Property changed! 4
Property changed! 4
參考被刪除後它也引發了相當長一段時間。但是,真正的泄漏是可以避免的,因爲它確實似乎最終被刪除,也許經過一輪垃圾收集?
這是預期的行爲?即弱事件監聽器在觸發事件之前沒有主動檢查沒有其他引用,而只是檢查該對象是否已被垃圾收集(可能發生這種情況,因爲它只是一個弱引用,所以最終會發生)?
通常情況下,這不會是一個問題,除非我有一個案例,我正在做一些非常昂貴的操作(獲取圖形的數據),因爲有一些變化,在一些不容易跟蹤(一個非常交互式的用戶界面,用戶可以在佈局上拖放東西)。我可以實現IDisposable或類似的,但它會是一個很大的重構。
因此,一個解決方案是在觸發這個昂貴的更改之前強制垃圾收集,但不確定這是很好的做法。 – Joe
不是。如果您想立即取消訂閱該事件,則應該使用強參考:https://stackoverflow.com/questions/43201093/is-it-safe-to-replace-all-standard-event-handler to-weakeventmanager-or-its-vari/43204926#43204926 – mm8
我很害怕。謝謝。正確的,大的IDisposable重構時間! – Joe