2010-06-15 40 views
8

我剛剛完成使用MVVM模式編寫WPF和c#的桌面應用程序。在這個應用程序中,我使用委託命令實現來包裝我的ModelView中公開的ICommands屬性。問題是這些DelegateCommands阻止我的ModelView和View在關閉視圖後被垃圾回收。所以它一直在讀書,直到我終止整個申請。我剖析了應用程序,我發現這完全是關於將modelview保存在內存中的delegatecommand。 我怎麼能避免這種情況,這是mvvm模式的性質,還是它關於我的模式植入?謝謝。由於DelegateCommand WPF應用程序中的內存泄漏

編輯:這是我如何實現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); 
    } 
} 

二:模型視圖類

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> 

+0

你應該真的使用'Func '來代替'Predicate '... http://stackoverflow.com/questions/665494/why-funct-bool-instead-of-predicatet – 2013-12-03 03:15:43

回答

8

在你的情況,包括什麼什麼參考?

  1. DelegateCommand包含ViewModel參考 - 其executecanExecute屬性包含到ViewModel實例的方法引用。

  2. ViewModel包含對DelegateCommand的引用 - 它的PrintCommand屬性。

  3. 該視圖包含對ViewModel的任意數量的引用。

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

這最後引用是一種特殊情況:CommandManager在其RequerySuggested活動使用WeakReference,所以儘管DelegateCommand寄存器這種情況下,它仍然是垃圾收集。

鑑於這一切,你應該沒有問題。如果視圖被丟棄,ViewModelDelegateCommand都不應該可達。

你說你已經介紹了應用程序,DelegateCommand持有對ViewModel的引用。在我看來,下一個合乎邏輯的問題應該是:什麼是對DelegateCommand的引用?它不應該是CommandManager。你的應用程序中是否有其他引用你的命令的東西?

+0

@Robert Rossney:非常優秀的一點,但如何綁定我在視圖中做的屬性和命令來自ViewModel,它將訂閱PropertyChanged事件來通知,這些將由WPF框架管理?這些綁定操作有沒有關注?謝謝.. – 2010-06-16 06:35:06

+0

爲了明確地回答這個問題,我不得不使用Reflector進行探討,但我相當確定該綁定僅包含對其源和目標的引用,並且沒有其他引用綁定。因此,如果垃圾收集器無法從根中找到視圖或視圖模型,那麼它們既是引用又是綁定引用的事實不會影響它們是否被垃圾收集。但是你正在使用一個分析器:它是否告訴你什麼是對'DelegateCommand'的引用? – 2010-06-16 07:30:10

+0

是的,它告訴我DelegateCommand在ModelView中有一個引用並標記爲WeakReference,但它們從未被收集,我知道因爲我離開配置文件運行一段時間並拍攝新快照,我在那裏找到它們。實現IDisposible並將所有命令設置爲null將結果處理爲空。 繼續... – 2010-06-16 08:56:42

1

在閱讀本文後,我發現有一些相關信息的網頁。這是CodePlex上的一個名爲Memory Leak caused by DelegateCommand.CanExecuteChanged Event的頁面。

通過報道:huetter
更新:dschenkelman

如果在分析我的應用我注意到,很多事件處理器 的從未從DelegateCommand的 CanExecuteChanged,事件註銷。所以那些EventHandlers從來都不是垃圾收集器,這導致了嚴重的內存泄漏。

註冊CanExecuteChanged-EventHandles是在 應用程序範圍之外完成的,我期望它們也自動註銷 。在這一點上,我認爲這不妨是 ThirdParty WPF控制問題,但進一步挖掘我讀博客 後,指出「WPF預計ICommand.CanExecuteChanged事件 應用WeakReferences的EventHandlers」。我查看了 RoutedCommand,並注意到它也使用了WeakReferences。

我調整了DelegateCommand以使用類似於 RoutedCommand的CanExecuteChanged-Event的實現,並且內存泄漏消失了 。 CompositeCommand也是如此。

已關閉2009年11月3日下午6:28由於此問題在 Prism-v2.1版本中修復,所以Workitem現在已關閉。棱鏡2.1可以從這裏下載 :
http://www.microsoft.com/downloads/details.aspx?FamilyID=387c7a59-b217-4318-ad1b-cbc2ea453f40&displaylang=en

1

我認爲,在這個代碼中有,這是造成視圖模型永遠不會被垃圾收集循環引用。

我知道這是一個老問題,但我會指出DelegateCommand或RelayCommand的某些實現擁有對該操作的WeakReference。在這裏使用DelegateCommand是典型的,但不幸的是會導致這個實現的內存泄漏,因爲當ViewModel的方法被傳入DelegateCommand的構造函數時,委託會自動捕獲包含該方法的類的引用。

如果您在ViewModel上實現了IDispose並在Dispose中清除了對DelegateCommands的引用,那麼您可以繼續使用此實現。然而,您構建ViewModel的視圖也必須使用Dipose。

+0

這裏的問題不是'DelegateCommand'阻止'ViewModel'被垃圾收集,但是某些東西(也許'WPF Control'或'ViewModel'阻止'DelegateCommand'被垃圾回收)。 – Lightman 2015-09-08 16:50:17

+1

是的,這就是爲什麼添加Dispose到ViewModel以允許ViewModel擺脫它對DelegateCommand的引用可能會有所幫助的原因。 DelegateCommand和ViewModel之間肯定有一個循環引用,但是這兩者中的任何一個是否都是根源,並且阻止垃圾收集,我不知道。 – Malaise 2015-09-09 17:44:13