2010-06-15 57 views
8

Tôi vừa hoàn thành các ứng dụng dành cho máy tính để bàn được viết bằng WPF và C# sử dụng mẫu MVVM. Trong ứng dụng này, tôi đã sử dụng thực thi Lệnh Delegate để bọc các thuộc tính ICommands được hiển thị trong ModelView của tôi. Vấn đề là các DelegateCommands này ngăn cản ModelView và View của tôi khỏi bị thu gom rác sau khi đóng khung nhìn. Vì vậy, nó vẫn larking cho đến khi tôi chấm dứt toàn bộ ứng dụng. Tôi hồ sơ ứng dụng tôi tìm thấy tất cả về delegatecommand rằng giữ modelview trong bộ nhớ. Làm cách nào để tránh tình trạng này và điều này có tính chất mvvm, hay là do việc cấy ghép mô hình của tôi ?. Cảm ơn.Rò rỉ bộ nhớ trong ứng dụng WPF do DelegateCommand

Edit: đây là phần nhỏ nhưng đầy đủ về cách tôi thực hiện MVVM pattern

Đầu tiên: lớp 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); 
    } 
} 

Thứ hai: ModelView Lớp

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

Thứ ba: Mã Window đằng sau

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

Forth: XAML My

<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

Bạn thực sự nên sử dụng 'Func ' thay vì 'Predicate ' ... http://stackoverflow.com/questions/665494/why-funct-bool-instead-of-predicatet –

Trả lời

8

Trong trường hợp của bạn, những gì chứa một tham chiếu đến những gì?

  1. DelegateCommand chứa một tham chiếu đến ViewModel - nó executecanExecute tính chứa tham chiếu đến một phương pháp của ViewModel dụ.

  2. ViewModel chứa tham chiếu đến DelegateCommand - thuộc tính PrintCommand của nó.

  3. Chế độ xem chứa bất kỳ số tham chiếu nào đến ViewModel.

  4. CommandManager chứa tham chiếu đến DelegateCommand trong sự kiện RequerySuggested của nó.

Đó tài liệu tham khảo cuối cùng là một trường hợp đặc biệt: CommandManager sử dụng một WeakReference trong trường hợp RequerySuggested của nó, vì vậy mặc dù thực tế rằng DelegateCommand đăng ký cho sự kiện đó, nó vẫn có thể được thu gom rác.

Với tất cả điều này, bạn không nên gặp sự cố. Nếu chế độ xem được xử lý, không thể truy cập được ViewModel cũng như DelegateCommand.

Bạn nói bạn đã lược tả đơn đăng ký và DelegateCommand đang giữ tham chiếu đến ViewModel. Dường như với tôi rằng câu hỏi tiếp theo hợp lý nên là: những gì đang nắm giữ một tham chiếu đến DelegateCommand? Nó không được là CommandManager. Bạn có cái gì khác trong ứng dụng của bạn đó là tham chiếu đến lệnh của bạn?

+0

@Robert Rossney: rất tuyệt vời để bắt đầu với, nhưng làm thế nào về ràng buộc tôi làm trong xem để tài sản và lệnh đến từ ViewModel mà sẽ đăng ký để PropertyChanged sự kiện được thông báo, là những sẽ được quản lý bởi khuôn khổ WPF? Có mối quan tâm nào từ các hoạt động ràng buộc này không? Cảm ơn .. –

+0

Để trả lời câu hỏi đó một cách dứt khoát, tôi phải poke xung quanh với Reflector, nhưng tôi chắc chắn rằng ràng buộc chỉ chứa tham chiếu đến nguồn và đích của nó, và không có gì khác có tham chiếu tới ràng buộc. Vì vậy, nếu người thu gom rác không thể tìm thấy mô hình xem hoặc xem từ gốc, thực tế là cả hai tham chiếu và tham chiếu bởi ràng buộc sẽ không ảnh hưởng đến việc chúng có được thu gom rác hay không. Nhưng bạn đang sử dụng một profiler: nó cho bạn biết những gì có một tham chiếu đến 'DelegateCommand'? –

+0

Có, nó nói với tôi rằng DelegateCommand có một tham chiếu trong ModelView và được đánh dấu là WeakReference nhưng chúng không bao giờ được thu thập, tôi biết rằng vì tôi để hồ sơ chạy trong một thời gian và chụp ảnh mới tôi tìm thấy chúng ở đó.Triển khai IDisposible và thiết lập tất cả lệnh thành null trong Dispose result thành không có gì. Tiếp tục ... –

1

Sau khi đọc bài đăng này, sau đó tôi đã xem một trang web có một số thông tin liên quan. Nó là một trang trên CodePlex được gọi là Memory Leak caused by DelegateCommand.CanExecuteChanged Event.

Báo cáo theo: Huetter
Cập nhật bởi: dschenkelman

Khi profiling ứng dụng của tôi, tôi nhận thấy rằng rất nhiều EventHandlers chưa bao giờ được deregistered từ DelegateCommand của CanExecuteChanged-tổ chức sự kiện. Vì vậy, những EventHandlers không bao giờ là bộ thu gom rác, gây ra rò rỉ bộ nhớ nghiêm trọng.

Khi đăng ký CanExecuteChanged-EventHandles được thực hiện bên ngoài phạm vi mã ứng dụng, tôi đã mong đợi họ sẽ bị hủy đăng ký tự động. Tại thời điểm này tôi nghĩ rằng điều này cũng có thể là một vấn đề kiểm soát WPP ThirdParty, nhưng đào sâu hơn tôi đọc một bài viết blog nói rằng "WPF hy vọng ICommand.CanExecuteChanged-Sự kiện để áp dụng WeakReferences cho EventHandlers". Tôi đã có một cái nhìn vào RoutedCommand, và nhận thấy nó sử dụng WeakReferences là tốt.

Tôi đã điều chỉnh DelegateCommand để sử dụng triển khai tương tự như Sự kiện CanExecuteChanged của RoutedCommand và rò rỉ bộ nhớ là đã biến mất. Điều này cũng đúng với CompositeCommand.

Đóng cửa ngày 3 tháng 11 năm 2009 lúc 6:28 chiều bởi Sự cố này đã được khắc phục trong bản phát hành Prism-v2.1, do đó Workitem hiện đã đóng. Prism 2.1 có thể được tải về từ đây:
http://www.microsoft.com/downloads/details.aspx?FamilyID=387c7a59-b217-4318-ad1b-cbc2ea453f40&displaylang=en

1

Tôi nghĩ rằng trong mã này có một tham chiếu vòng tròn đó đang gây ra ViewModel để không bao giờ được thu gom rác thải.

Tôi biết đây là một câu hỏi cũ, nhưng tôi sẽ chỉ ra rằng một số triển khai của DelegateCommand hoặc RelayCommand giữ một WeakReference đối với hành động. Việc bạn sử dụng DelegateCommand ở đây là điển hình, nhưng không may sẽ gây rò rỉ bộ nhớ với việc triển khai này bởi vì khi phương thức của ViewModel được truyền vào hàm tạo của DelegateCommand, tham chiếu đến lớp chứa phương thức đó sẽ được đại biểu tự động bắt.

Nếu bạn triển khai IDispose trên ViewModel của mình và xóa các tham chiếu đến DelegateCommands một cách rõ ràng trong Dispose, thì bạn có thể tiếp tục sử dụng triển khai này. Quan điểm của bạn đang xây dựng ViewModel của bạn cũng sẽ phải Dipose của nó, tuy nhiên.

+0

Vấn đề ở đây không phải là 'DelegateCommand' ngăn chặn' ViewModel' khỏi bị thu gom rác, nhưng một điều gì đó (có thể 'WPF Control' hoặc' ViewModel' ngăn chặn 'DelegateCommand' khỏi bị thu gom rác). – Lightman

+1

Vâng, đó là lý do tại sao thêm Vứt bỏ vào ViewModel, để cho phép ViewModel thoát khỏi tham chiếu của nó tới DelegateCommand có thể hữu ích. Chắc chắn có một tham chiếu vòng tròn ở đây giữa DelegateCommand và ViewModel, nhưng liệu một trong hai thứ đó có bắt rễ và ngăn chặn việc thu gom rác tôi không biết. – Malaise