【问题标题】:Prism's DelegateCommand memory leakPrism 的 DelegateCommand 内存泄漏
【发布时间】:2016-03-15 07:23:50
【问题描述】:

使用此代码,您可以 100% 地重现我的问题。我创建了一个“父”视图模型:

using Prism.Commands;
using Prism.Mvvm;
using System.Collections.ObjectModel;

namespace WpfApplication1 {
    public class ViewModel : BindableBase {

        public ObservableCollection<ChildViewModel> Items { get; set; }

        public DelegateCommand<ChildViewModel> CloseItem { get; set; }

        public ViewModel() {
            CloseItem = new DelegateCommand<ChildViewModel>(OnItemClose);

            Items = new ObservableCollection<ChildViewModel>(new[] {
                new ChildViewModel { Name = "asdf" },
                new ChildViewModel { Name = "zxcv" },
                new ChildViewModel { Name = "qwer" },
                new ChildViewModel { Name = "fdgz" },
                new ChildViewModel { Name = "hgjkghk" }
            });
        }

        private void OnItemClose(ChildViewModel obj) {
            Items.Remove(obj);
        }
    }
}

其中包含ObservableCollectionChildViewModel 实例:

using Prism.Mvvm;

namespace WpfApplication1 {
    public class ChildViewModel : BindableBase {

        public string Name { get; set; }
        protected byte[] leakArr = new byte[1024 * 1024 * 10];

    }
}

如您所见,我在每个 ChildViewModel 中声明了一个 10mb 数组。现在的观点:

public partial class MainWindow : Window {
    public MainWindow() {
        InitializeComponent();

        DataContext = new ViewModel();
    }
}

<Window x:Class="WpfApplication1.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"
        xmlns:local="clr-namespace:WpfApplication1"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <TabControl ItemsSource="{Binding Items}">
        <TabControl.ItemTemplate>
            <DataTemplate>
                <DockPanel>
                    <TextBlock Text="{Binding Path=Name}" DockPanel.Dock="Left" Margin="2,3,0,2" />
                    <Button DockPanel.Dock="Right" BorderThickness="0" Background="Transparent" 
                            Margin="10,2,2,2" 
                            Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TabControl}}, Path=DataContext.CloseItem}" 
                            CommandParameter="{Binding}">
                        <Button.Content>
                            <TextBlock FontWeight="Bold" Text="X" />
                        </Button.Content>
                    </Button>
                </DockPanel>
            </DataTemplate>
        </TabControl.ItemTemplate>
    </TabControl>
</Window>

查看 VS2015 内存诊断工具,我可以看到,即使在从 Items 集合中删除每个 ChildViewModel 之后,它仍然存在于内存中,并且一个对象(对于每个 vm)仍然持有对它的引用 - @987654329 @ 控制。有趣的是,如果我将Button 的命令声明替换为:

Click="Button_Click"

private void Button_Click(object sender, RoutedEventArgs e) {
            (DataContext as ViewModel).CloseItem.Execute(((sender as Button).DataContext as ChildViewModel));
        }

没有内存泄漏 - 这意味着调试工具没有显示任何已处理的 ChildViewModel 实例。我不知道这是 Prism 特有的问题,还是某种 wpf 怪癖。

我正在使用最新版本的 .net 和 prism 库。

【问题讨论】:

  • 出于好奇,如果您删除 CommandParameter="{Binding}" 部分会怎样?它是否仍然存在内存泄漏?
  • 没有。正如我上面写的,如果我用 ClickEvent 替换 Command 它工作正常。
  • 我的意思是,如果您保留命令但只删除 CommandParameter="{Binding}" 部分,它仍然会出现内存泄漏吗?
  • 没有CommandParameter它不会表现出内存泄漏。
  • CommandParameter="{Binding}" 创建从按钮到绑定视图模型的引用,看起来它在从集合中删除项目后被保留。您真的需要 CommandParameter="{Binding}" 部分还是可以切换以从 TabControl.SelectedItem 获取当前视图模型?

标签: c# wpf memory-leaks prism


【解决方案1】:

您是否检查了自定义 ICommand 实现是否具有相同的行为?

由于相关的错误,我们可能在删除弱引用时引入了这种内存泄漏。

您能否在此处将其报告为问题:

https://github.com/PrismLibrary/Prism/issues

我们会调查并调查。

编辑:我很高兴听到这不是 Prism 问题 :)

【讨论】:

  • 我没有使用自定义 ICommand 实现检查它。明天我会报告的。感谢您的回答!
【解决方案2】:

看来我找到了解决这个问题的方法(至少在我的应用程序中)——我停止直接绑定到 ChildViewModel 实例,而是将 CommandParameter 绑定到 Name 的 @987654325 属性@。然后,在ViewModel 中,我只是通过 Items 集合并删除具有匹配属性值的项目。现在 VS 诊断工具没有显示任何应该被 GC 处理但没有被 GC 处理的对象,因此内存泄漏消失了。

Prism 团队正在分析这个问题:https://github.com/PrismLibrary/Prism/issues/345

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-04-30
    • 1970-01-01
    • 1970-01-01
    • 2012-07-06
    • 1970-01-01
    • 1970-01-01
    • 2019-07-05
    相关资源
    最近更新 更多