11

我刚刚使用 MVVM 模式完成了用 WPF 和 c# 编写的桌面应用程序。在这个应用程序中,我使用了 Delegate Command 实现来包装在我的 ModelView 中公开的 ICommands 属性。问题是这些 DelegateCommands 阻止我的 ModelView 和 View 在关闭视图后被垃圾收集。所以它一直在闲逛,直到我终止整个应用程序。我对应用程序进行了概要分析,我发现这完全是关于将模型视图保存在内存中的委托命令。我怎样才能避免这种情况,这是 mvvm 模式的本质,还是与我植入该模式有关?谢谢。

编辑:这是我如何实现 MVVM 模式的一小部分但完整的部分

第一个:CommandDelegte 类

class DelegateCommand:ICommand
{
    private Action<object> execute;
    private Predicate<object> canExcute;
    public DelegateCommand(Action<object> execute, Predicate<object> canExecute)
    {
        if (execute == null)
        {
            throw new ArgumentNullException("execute");
        }
        this.execute = execute;
        this.canExcute = canExecute;
    }
    public bool CanExecute(object parameter)
    {
        if (this.canExcute != null)
        {
            return canExcute(parameter);
        }
        return true;
    }

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }


    public void Execute(object parameter)
    {
        this.execute(parameter);
    }
}

二:ModelView类

public class ViewModel:DependencyObject, INotifyPropertyChanged
{
    private DelegateCommand printCommand;

    public ICommand PrintCommand
    {
        get
        {
            if (printCommand == null)
            {
                printCommand = new DelegateCommand(Print, CanExecutePrint);
            }
            return printCommand;
        }
    }
    void Print(object obj)
    {
        Console.WriteLine("Print Command");

    }
    bool CanExecutePrint(object obj)
    {
        return true;
    }


    public event PropertyChangedEventHandler PropertyChanged;
    private void OnProeprtyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

三:后面的窗口代码

public MainWindow()
    {
        InitializeComponent();
        base.DataContext = new ViewModel();
    }

第四:我的 XAML

<Window x:Class="WpfApplication1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">
<Window.InputBindings>
    <KeyBinding Key="P" Modifiers="Control" Command="{Binding Path=PrintCommand}"/>
</Window.InputBindings>
<StackPanel>
    <Button Content="Print - Ctrl+P" Width="75" Height="75" Command="{Binding Path=PrintCommand}"/>
</StackPanel>

4

3 回答 3

9

在您的情况下,什么包含对什么的引用?

  1. DelegateCommand包含对ViewModel- 它的引用executecanExecute属性包含对ViewModel实例方法的引用。

  2. ViewModel包含DelegateCommand对其PrintCommand属性的引用。

  3. 该视图包含任意数量的对ViewModel.

  4. CommandManager包含DelegateCommand在其RequerySuggested事件中的引用。

最后一个引用是一种特殊情况: 在其事件中CommandManager使用 a ,因此尽管注册了该事件,但它仍然可以被垃圾收集。WeakReferenceRequerySuggestedDelegateCommand

鉴于这一切,你不应该有问题。如果视图被释放,则 theViewModel和 theDelegateCommand都不应该被访问。

您说您已对应用程序进行了概要分析并DelegateCommand持有对ViewModel. 在我看来,合乎逻辑的下一个问题应该是:什么是对 的引用DelegateCommand?不应该CommandManager。您的应用程序中是否还有其他引用您的命令的内容?

于 2010-06-15T17:53:57.767 回答
1

读完这篇文章后,我发现了一个包含一些相关信息的网页。它是 CodePlex 上一个名为Memory Leak 的页面,由 DelegateCommand.CanExecuteChanged Event 引起

报告人:huette
更新人:dschenkelman

在分析我的应用程序时,我注意到很多 EventHandlers 从未从 DelegateCommand 的 CanExecuteChanged-Event 中注销。所以那些 EventHandlers 从来都不是垃圾收集器,这导致了严重的内存泄漏。

由于注册 CanExecuteChanged-EventHandles 是在应用程序代码范围之外完成的,因此我预计它们也会自动注销。在这一点上,我认为这也可能是第三方 WPF 控制问题,但进一步挖掘我阅读了一篇博客文章,指出“WPF 期望 ICommand.CanExecuteChanged-Event 为 EventHandlers 应用 Wea​​kReferences”。我查看了 RoutedCommand,并注意到它也使用了 WeakReferences。

我调整了 DelegateCommand 以使用类似于 RoutedCommand 的 CanExecuteChanged-Event 的实现,并且内存泄漏消失了。CompositeCommand 也是如此。

于 2009 年 11 月 3 日下午 6:28 关闭 此问题已在 Prism-v2.1 版本中修复,因此 Workitem 现已关闭。Prism 2.1 可以从这里下载: http:
//www.microsoft.com/downloads/details.aspx? FamilyID=387c7a59-b217-4318-ad1b-cbc2ea453f40&displaylang=en

于 2013-04-07T15:57:41.150 回答
1

我认为在这段代码中有一个循环引用导致 ViewModel 永远不会被垃圾收集。

我知道这是一个老问题,但我会指出,DelegateCommand 或RelayCommand的某些实现对操作持有 WeakReference。您在此处使用 DelegateCommand 是典型的,但不幸的是,此实现会导致内存泄漏,因为当 ViewModel 的方法被传递到 DelegateCommand 的构造函数时,委托会自动捕获对包含该方法的类的引用。

如果您在 ViewModel 上实现了 IDispose 并在 Dispose 中明确清除了对 DelegateCommands 的引用,那么您可以继续使用此实现。但是,构建 ViewModel 的视图也必须处理它。

于 2014-03-17T07:53:21.723 回答