2010-06-15 11 views
8

Właśnie skończyłem aplikacje napisane w WPF i C# przy użyciu wzorca MVVM. W tej aplikacji użyłem implementacji Command Delegate, aby zawinąć właściwości ICommands w moim ModelView. Problem polega na tym, że te polecenia DelegateCommands uniemożliwiają usunięcie widoku ModelView i widoku ze śmieci po zamknięciu widoku. Pozostaje więc żartować, dopóki nie zakończę całej aplikacji. Profiluję aplikację Uważam, że chodzi o delegatecommand, która utrzymuje modelowanie w pamięci. Jak mogę uniknąć tej sytuacji i czy to jest w naturze wzoru mvvm, czy chodzi tu o wszczepienie wzoru ?. Dzięki.Przeciek pamięci w aplikacji WPF z powodu DelegateCommand

Edit: jest mała, ale pełna porcja jak zaimplementować MVVM wzór

pierwsze: klasa 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); 
    } 
} 

drugie: ModelView Klasa

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)); 
     } 
    } 
} 

trzecie: Kod Okno za

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

Forth: Mój 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

Powinieneś naprawdę użyć 'Func ' zamiast 'Predicate ' ... http://stackoverflow.com/questions/665494/why-funct-bool-instead-of-predicatet –

Odpowiedz

8

W twoim przypadku, co zawiera odniesienie do tego, co?

  1. DelegateCommand zawiera odniesienie do ViewModel - jego execute i canExecute właściwości zawierają odniesienia do metod przykład ViewModel.

  2. ViewModel zawiera odniesienie do DelegateCommand - jego własność PrintCommand.

  3. Widok zawiera dowolną liczbę odniesień do ViewModel.

  4. Numer CommandManager zawiera odniesienie do DelegateCommand w swoim zdarzeniu RequerySuggested.

To ostatnie odniesienie jest szczególnym przypadkiem: CommandManager używa WeakReference w RequerySuggested razie tak pomimo faktu, że DelegateCommand rejestry dla tego wydarzenia, to może jeszcze być śmieci zebrane.

Biorąc to wszystko pod uwagę, nie powinieneś mieć problemu. Jeśli widok zostanie usunięty, ani ViewModel, ani DelegateCommand nie powinny być osiągalne.

Mówisz, że profilujesz aplikację, a DelegateCommand zawiera odniesienie do ViewModel. Wydaje mi się, że następnym logicznym pytaniem powinno być: co zawiera odniesienie do DelegateCommand? To nie powinno być CommandManager. Czy masz coś innego w swojej aplikacji, które odwołuje się do twoich poleceń?

+0

@Robert Rossney: bardzo dobry punkt na początek, ale jak o wiązaniu zrobić w widoku właściwości i poleceń pochodzi od ViewModel, który zapisze się do zdarzenia PropertyChanged, aby zostać powiadomionym, czy będą one zarządzane przez framework WPF? Czy są jakieś obawy związane z tymi wiążącymi operacjami? Dzięki .. –

+0

Aby odpowiedzieć na to pytanie, zdecydowanie musiałbym zagłębiać się w Reflectora, ale jestem dość pewny, że wiązanie zawiera odniesienia tylko do jego źródła i celu, a nic więcej nie ma odniesienia do wiązania. Jeśli więc garbage collector nie może znaleźć widoku lub widoku modelu z katalogu głównego, fakt, że oba te odwołania i odniesienia się wiążą, nie ma wpływu na to, czy zostaną zebrane. Ale używasz profilera: czy mówi ci, co ma odniesienie do 'DelegateCommand'? –

+0

Tak, mówi mi, że DelegateCommand ma odwołanie w ModelView i oznaczony jako WeakReference, ale nigdy nie są zbierane, wiem, że ponieważ zostawiam profil uruchomiony przez jakiś czas i zrobię nową migawkę, znajdę je tam.Wdrażanie IDisposible i ustawienie wszystkich poleceń na null w Dispose result to nothing. Kontynuuj ... –

1

Po przeczytaniu tego posta, natknąłem się na stronę internetową zawierającą pewne powiązane informacje. Jest to strona na CodePlex o nazwie Memory Leak caused by DelegateCommand.CanExecuteChanged Event.

Zgłoszone przez: HUETTER
uaktualnione przez: dschenkelman

Podczas profilowania mojej aplikacji Zauważyłem, że wiele EventHandlers nigdy nie została wyrejestrowana z DelegateCommand za CanExecuteChanged-Event. Więc te EventHandlers nigdy nie były zbieraczem śmieci, co spowodowało poważny wyciek pamięci.

Podczas rejestracji CanExecuteChanged-EventHandles jest wykonywany poza zakresem kodu aplikacji i oczekiwałem, że zostaną automatycznie wyrejestrowane . W tym momencie pomyślałem, że równie dobrze może to być problem kontrolny ThirdParty WPF, ale jeszcze głębiej czytam bloga po stwierdzeniu, że "WPF oczekuje, że ICommand.CanExecuteChanged-Event zastosuje WeakReferences dla EventHandlers". Zajrzałem do RoutedCommand i zauważyłem, że używa również WeakReferences.

Dostosowałem DelegateCommand do użycia implementacji podobnej do RoutedCommand's CanExecuteChanged-Event, a wyciek pamięci został usunięty . To samo dotyczy CompositeCommand.

Zamknięto 3 listopada 2009 o 18:28 przez Ten problem został naprawiony w wersji Prism-v2.1, więc Workitem jest teraz zamknięty. Prism 2.1 można pobrać stąd:
http://www.microsoft.com/downloads/details.aspx?FamilyID=387c7a59-b217-4318-ad1b-cbc2ea453f40&displaylang=en

1

myślę, że w tym kodzie jest odwołanie cykliczne powodującemu ViewModel, aby nigdy nie być zbierane śmieci.

Wiem, że jest to stare pytanie, ale zwrócę uwagę, że niektóre implementacje DelegateCommand lub RelayCommand wstrzymują działanie WeakReference. Używanie tutaj DelegateCommand jest typowe, ale niestety spowoduje wycieki pamięci z tą implementacją, ponieważ kiedy metoda ViewModel zostanie przekazana do konstruktora DelegateCommand, odwołanie do klasy zawierającej tę metodę jest automatycznie przechwytywane przez delegata.

Jeśli zaimplementowałeś IDispose na swoim ViewModelu i wyraźnie usunąłeś odwołania do DelegateCommands w Dispose, możesz kontynuować korzystanie z tej implementacji. Twój widok, który buduje Twój ViewModel, musiałby jednak również Dipose of.

+0

Problemem tutaj nie jest "DelegateCommand" uniemożliwiające 'ViewModel' z bycie zebranym, ale to coś (być może' Kontrola WPF' lub 'ViewModel' zapobieganie' DelegateCommand' przed zbieraniem śmieci). – Lightman

+1

Tak, dlatego dodanie opcji Unieś do ViewModel, aby umożliwić ViewModel pozbyć się jego odwołania do DelegateCommand może pomóc. Z pewnością istnieje tu cykliczne odwołanie między DelegateCommand i ViewModel, ale jedno z nich jest zrootowane i zapobiegam wyrzucaniu śmieci, którego nie znam. – Malaise

Powiązane problemy