2012-10-16 2 views
7

저는 LINQ to SQL을 사용하여 SQL Server 데이터베이스에 연결하는 WPF 응용 프로그램을 작성하고 있습니다.WPF 응용 프로그램에서 UI 메모리 누수가 발생하는 ICommand 바인딩

앱의 기본 창에 일련의 상세보기가 포함 된 ListView이 있습니다. ListViewItemSource은 루트보기 모델에서 속성으로 표시되는 상세보기 모델 개체 모음에 바인딩됩니다. 각 상세보기 모델 객체는 여러 개의 ICommand 속성과 상세 모델 객체를 노출하는 속성을 구성하며 세부적인 객체는 UI에 표시된 다양한 데이터 필드를 노출합니다.

ANTS 메모리 프로파일 러를 사용한 분석 결과, 누설 된 개체가 상세 모델 개체에 포함 된 개체와 바인딩되는 일부 UI 클래스임을 알 수 있습니다. 이전의 새로 고침에서 이러한 개체의 인스턴스가 가비지 수집되지 않습니다.

ANTS에는 원치 않는 메모리가 유지되는 이유를 식별하기 위해 사용자가 참조 체인을 추적 할 수있는 도구가 있습니다. 그것을 사용할 때 나타나는 모든 체인은 ICommand입니다. 따라서 문제가되는 ICommand을 제거한 결과 메모리 누수가 사라지는 것을 발견했습니다.

불행히도 중요한 기능을 구현하려면 ICommand이 필요합니다. 실제로 나를 혼란스럽게하는 것은 세부 뷰 모델 객체에서 두 개의 완전히 별개의 인스턴스 변수 인, 상세 모델 객체에 대한 참조를 처음부터 어떻게 가지는지입니다.

다음은 자세히보기 모델 개체의 생성자입니다 (RootViewModel에 대한 참조는 ICommands에 연결된 일부 메서드의 콜백에 사용됩니다.) 원래는 이것이 참조의 순환 체인을 일으킬 수 있다고 생각했습니다. .을 제거하여 문제의 원인, 그러나 어떤 영향을하지 않는 것)

public CarDataViewModel(CarData carDataItem, RootViewModel parentViewModel) 
    { 

     _parentViewModel = parentViewModel; 
     CarDataModel = carDataItem; 
     CompetingCheckboxStatus = CarDataModel.CurrentCar.Competing; 
     AcknowledgeAlarm = new ParameterlessCommand(AcknowledgeAlarmClicked); 
     Acknowledge = new ParameterlessCommand(AcknowledgeClicked); 
     ShowReport = new ParameterlessCommand(ShowReportClicked); 
     Cancel = new ParameterlessCommand(CancelClicked); 
    } 

여기 바인딩이 설정되어있는 XAML의 - AcknowledgeAlarm는 ICommand의이다, CarDataModel는 세부 모델 객체는 다음과 같습니다

<ListView x:Name="itemGridView"Grid.Row="1"ScrollViewer.HorizontalScrollBarVisibility="Disabled" ItemsSource="{Binding CarDataViewModels}" IsSynchronizedWithCurrentItem="True" Margin="0,0,0,0"> 
     <ListView.ItemTemplate> 
      <DataTemplate> 
       </DataTemplate.Resources> 
       <Button Command="{Binding AcknowledgeAlarm}"> 
        <Border DataContext="{Binding CarDataModel}" BorderBrush="{StaticResource GrayFadeBrush}" Background="White" BorderThickness="5"> 
         <Grid> . . . 
+0

이것이 ParameterlessCommand의 CanExecuteChanged 이벤트와 관련이 있다고 생각됩니다. 어떻게 구현되는지 보여줄 수 있습니까? – Daniel

+0

그것은'의 공개 이벤트 핸들러 CanExecuteChanged은,''보호 된 가상 무효가 (있는 EventArgs 인수) { OnCanExecuteChanged 경우 (CanExecuteChanged = 널!) { CanExecuteChanged (이, 인수); } }'- 서식을 사용하여 죄송합니다. 반환 할 수없는 것으로 보입니다. –

답변

10

CanExecuteChanged 이벤트 핸들러가 누출과 관련이 있습니다.

WPF에서는 ICommand 구현이 이벤트 처리기에 약한 참조를 사용하기를 기대합니다. 이 누수의 원인이 될 수있는 강력한 참조를 사용하는 일반적인 .NET 이벤트를 사용하고 있습니다.

ParameterlessCommand 인스턴스를 만드는 방법은 CanExecute이 항상 사실임을 암시하는 것으로 보이며 이벤트를 전혀 필요로하지 않습니다. 실제로 어디서나 이벤트를 실행 하시겠습니까? 아니면 OnCanExecuteChanged 사용하지 않는 코드입니까?

public event EventHandler CanExecuteChanged { add {} remove {} } 

이벤트가 어떤 핸들러를 저장하지 않습니다이 방법을하고 뷰 모델은 UI 요소에 대한 강한 참조를 가진 방지 :

그렇지 않은 경우와 이벤트 정의를 교체합니다. 당신이 이벤트를 발생해야하는 경우

는 가장 쉬운 해결책은 ICommand의 예상 약한 이벤트 의미와 일치하는, CommandManager.RequerySuggested을 사용하는 것입니다

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

당신이해야 할 또 한가지에 INotifyPropertyChanged을 구현하는 것입니다 당신의 보기 모델 (아직 수행하지 않은 경우)을 사용하고 각 속성에 대해 개별 NameChanged 등의 이벤트를 사용하는 대신이를 사용하십시오. UI 요소 다시보기 모델에서 참조가있는 경우 개별 속성을 다루는 WPF의 논리는 메모리 누수가 발생하기 때문에 이다 : 당신은 당신이 실제로하지 않는 경우에도 INotifyPropertyChanged를 구현해야 AFAIK http://support.microsoft.com/kb/938416

모든 변경 이벤트.


내 생각 엔이 두 가지 문제 중 하나를 수정하는 누출을 사라지게 할 것입니다 : 잘못 INotifyPropertyChanged의 부족이 원인이되는 아래의 상황이 정확히 어떤 뷰 모델에서 강한 참조가 확인됩니다 CanExecuteChanged 구현 새는 곳.

하지만 두 가지 문제를 모두 해결하는 것이 좋습니다. 그들 중 하나가 아닙니다.

+0

고마워요! 그것은 그것을 분류했다. –

2

MVVM을 사용할 때 프로그래머가 처리해야하는 "새로운"문제 중 하나는 ViewModel이 자동으로 자체 정리하지 않는다는 것입니다. 뷰가로드되도록하는 속성을 설정 한 다음 해당 속성을 변경하면 이전 개체가 반드시 자동으로 삭제되지는 않습니다. 청소되기 전에 GC에 장시간 앉아있을 수도 있습니다.

LINQ를 사용하여 ViewModel 컬렉션을 만드는 것이 특히 유혹적이지만 매우 신중해야합니다. LINQ는 각 호출에서 개체의 새 인스턴스를 반환하여 잠재적으로 메모리 누수와 상태 문제를 만듭니다.

모두 내가 말했듯이 누출의 원인 (그리고 너무 많은 관련성이없는 정보)을 식별하는 데 도움이되는이 질문에 거의 정보가 없습니다. 가장 좋은 방법은 Factory 나 비슷한 것을 사용하여 ViewModel 생성 패턴을 표준화하는 것입니다. 그러면 의도 한대로 ViewModel의 새 인스턴스를 생성하고 인스턴스를 추적하여 필요에 따라 삭제할 수 있습니다.

관련 문제