Утечка памяти в приложении WPF из-за DelegateCommand - PullRequest
9 голосов
/ 15 июня 2010

Я только что закончил настольные приложения, написанные на WPF и c #, используя шаблон MVVM. В этом приложении я использовал реализацию Delegate Command, чтобы обернуть свойства ICommands, представленные в моем ModelView. Проблема в том, что эти 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 Class

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>

Ответы [ 3 ]

9 голосов
/ 15 июня 2010

В вашем случае, что содержит ссылку на что?

  1. DelegateCommand содержит ссылку на ViewModel - его свойства execute и canExecute содержат ссылки наметоды экземпляра ViewModel.

  2. ViewModel содержит ссылку на DelegateCommand - его свойство PrintCommand.

  3. представление содержит любое количество ссылок на ViewModel.

  4. . CommandManager содержит ссылку на DelegateCommand в своем событии RequerySuggested.

Эта последняя ссылка является особым случаем: CommandManager использует WeakReference в своем событии RequerySuggested, поэтому, несмотря на то, что DelegateCommand регистрируется для этого события, оно все равно может быть собрано мусором.

Учитывая все это, у вас не должно быть проблем.Если вид удаляется, ни ViewModel, ни DelegateCommand не должны быть доступны.

Вы говорите, что профилировали приложение, и DelegateCommand содержит ссылку на ViewModel.Мне кажется, что следующий логический вопрос должен звучать так: что содержит ссылку на DelegateCommand?Это не должно быть CommandManager.Есть ли в вашем приложении что-то еще, ссылающееся на ваши команды?

1 голос
/ 17 марта 2014

Я думаю, что в этом коде есть циклическая ссылка, которая заставляет ViewModel никогда не собирать мусор.

Я знаю, что это старый вопрос, но я укажу, что некоторые реализации DelegateCommand или RelayCommand содержат WeakReference для действия. Использование здесь DelegateCommand типично, но, к сожалению, в этой реализации произойдет утечка памяти, поскольку при передаче метода ViewModel в конструктор DelegateCommand ссылка на класс, содержащий этот метод, автоматически захватывается делегатом.

Если вы реализовали IDispose в своей ViewModel и явно очистили ссылки на DelegateCommands в Dispose, то вы можете продолжить использовать эту реализацию. Однако ваше представление, которое строит вашу ViewModel, также должно будет отказаться от него.

1 голос
/ 07 апреля 2013

После прочтения этого поста я наткнулся на веб-страницу, на которой была некоторая информация.Это страница в CodePlex с именем Утечка памяти, вызванная событием DelegateCommand.CanExecuteChanged .

Сообщено: huetter
Обновлено: dschenkelman

При профилированииВ моем приложении я заметил, что множество EventHandlers никогда не были отменены из CanExecuteChanged-Event от DelegateCommand.Таким образом, эти EventHandlers никогда не были сборщиком мусора, что вызывало серьезную утечку памяти.

Поскольку регистрация CanExecuteChanged-EventHandles выполняется вне области кода приложения, я ожидал, что они также будут автоматически отменены.В этот момент я подумал, что это может быть и проблема управления WPF ThirdParty, но, читая дальше, я прочитал сообщение в блоге, в котором говорится, что «WPF ожидает, что ICommand.CanExecuteChanged-Event будет применять WeakReferences для EventHandlers».Я заглянул в RoutedCommand и заметил, что он также использует WeakReferences.

Я адаптировал DelegateCommand для использования реализации, аналогичной CanExecuteChanged-Event от RoutedCommand, и утечка памяти исчезла.То же самое верно и для CompositeCommand.

Закрыто 3 ноября 2009 года в 18:28. Эта проблема была исправлена ​​в выпуске Prism-v2.1, поэтому Workitem теперь закрыт.Prism 2.1 можно скачать здесь:
http://www.microsoft.com/downloads/details.aspx?FamilyID=387c7a59-b217-4318-ad1b-cbc2ea453f40&displaylang=en

...