2013-02-23 3 views
1

MVVM 패턴을 사용하여 응용 프로그램을 구조화하려고합니다. 따라서 데이터가 변경 될 때 이벤트를 발생시키는 ViewModels가 있으며 UI는 해당 이벤트에 반응하고 보이는 UI 컨트롤을 업데이트해야합니다.UITableViewCells가 완전히 끝났을 때 감지

새로운 셀이 생성되거나 대기열에서 제외 될 때마다 특정 ViewModel로 초기화되는 파생 UITableViewCell이 있습니다 (miguel's example here과 매우 유사). 초기화의 일부인 주요 차이점 중 하나는 ViewModel의 이벤트를 구독하는 것입니다. 이렇게하면 오래 지속 된 ViewModel에서이 특정 셀에 대한 참조가 만들어지며 ViewModel의 수명 동안 메모리에 유지됩니다. 셀을 재사용하면 이전 구독이 정리되고 새 ViewModel에 새 구독이 만들어져 정상적으로 작동합니다.

그러나 문제는 셀이 완전히 완료되면 마지막 구독을 정리할 수있는 기회가없는 것입니다. 즉, ViewModel의 수명 동안 메모리에 보관됩니다 (원하는 것보다 훨씬 오래 걸림). 되려고). '완전히 완료되었습니다'는 VC 계층 구조에 따라 다르지만이 경우 사용자 지정 셀이있는 TableView가 포함 된 파생 된 DialogViewController가 UINavigationController 스택에서 팝되어 삭제되었습니다.

willMoveToSuperview은 결코 호출되지 않습니다. ('null'이 전달되기를 기대했습니다). removeFromSuperview은 호출되지 않습니다. 각 셀이 절대로 호출되지 않습니다. UITableViewController를 삭제해도 각 셀이 처리되지 않습니다. 컨트롤러 내에서 TableView를 삭제해도 각 셀이 처리되지 않습니다.

수동으로 각 셀을 폐기 할 수있는 유일한 방법은 수동으로 셀을 열거하여 수동으로 직접 파생 된 UIViewControllers에서 피하고 싶은 것입니다.

이와 비슷한 문제가있는 사람이 있습니까? UITableViewCells에서 MVVM 패턴을 처음 사용하는 것은 불가능합니다. 기본 MonoTouch UIKit 래퍼에 Dispose 패턴이있는 버그입니까?

편집 : 다음은 사용자 정의 UITableViewCells 중 하나의 자르기 버전입니다. 참고 UI의 모든 속성을 전체 MVVM에 바인딩하는 것이 아니라 변경할 수있는 속성의 이벤트를 명시 적으로 구독하는 실용적인 방법을 취하고 있습니다.

public class MyCustomCell : UITableViewCell 
{ 
    private InvoiceViewModel currentViewModel; 

    private readonly UILabel label1; 
    private readonly UILabel label2; 

    public MyCustomCell(NSString reuseId) 
     : base(UITableViewCellStyle.Default, reuseId) 
    { 
     Accessory = UITableViewCellAccessory.DisclosureIndicator; 
     SelectedBackgroundView = new UIView() 
     { 
      BackgroundColor = UIColor.FromRGB(235,235,235), 
     }; 

     label1 = new UILabel(); 
     ContentView.Add(label1); 
     // The rest of the UI setup... 
    } 

    public void Update(MyViewModel viewModel) 
    { 
     if (currentViewModel == viewModel) 
      return; 

     if (currentViewModel != null) 
     { 
      // Cleanup old bindings. 
      currentViewModel.UnacknowledgedRemindersChanged -= HandleNotificationsChanged; 
     } 

     currentViewModel = viewModel; 

     if (viewModel != null) 
     { 
      viewModel.UnacknowledgedRemindersChanged += HandleNotificationsChanged; 

      label1.Text = viewModel.SomeProperty; 
      // Update the rest of the UI with the current view model. 
     } 
    } 

    private void HandleNotificationsChanged() 
    { 
     // Event can fire on background thread. 
     BeginInvokeOnMainThread(() => 
     { 
      // Relevant UI updates go here. 
     }); 
    } 

    protected override void Dispose(bool disposing) 
    { 
     // Unsubscribes from ViewModel events. 
     Update(null); 
     base.Dispose(disposing); 
    } 
} 

그리고 내 파생 MT.D 요소 클래스는 1이 있습니다 : 그래서 내 바인딩 코드는 표준 이벤트 구독 구성 1 요소 : 뷰 모델을, 그래서 GetCell 방법은 다음과 같습니다

public override UITableViewCell GetCell (UITableView tv) 
    { 
     var cell = (MyCustomCell) tv.DequeueReusableCell(key); 
     if (cell == null) 
      cell = new MyCustomCell(key); 

     cell.Update(viewModel); 
     return cell; 
    } 

답변

1

MonoTouch로 Mvvm 테이블 셀을 처음으로 수행 한 것은 아닙니다.

내가 NDC에서 그가 있었다 프로젝트 전에 http://slodge.blogspot.co.uk/2013/01/uitableviewcell-using-xib-editor.html

에서 최근에 그것에 대해 블로그했습니다 ("노르웨이의 발"로 검색) 및 MonoTouch.Dialog 위에 구축 긴 실행 MVVM 프로젝트가 있었다.


MvvmCross 앱에서는 ObservableCollections 및 다른 IList 클래스에 바인딩 된 테이블을 많이 사용합니다.

이 중에서 우리는 일반적으로 왼쪽 - 거실 참조에 많은 문제를 일으키지 않습니다.하지만 우리는 일반적으로 긴 수명의 ViewModel을 사용하도록 권장하지 않기 때문에 - 우리는 함께 갈 ViewModel 데이터의 새 인스턴스를 만들려고합니다. 각보기. 그러나 모든 적용에 적합하지 않을 수 있음을 이해합니다.

  • 이 iOS5를 우리가 바인딩을 정리하기 위해 ViewDidUnload 방법을 사용했다 - 그러나 :

    MVX 사용자가 이러한 유형의 문제와 함께 스스로를 발견

    , 우리가 시도한 방법 중 일부입니다 분명히 지금은 iOS6에서 사라졌습니다.
  • 우리가 수동으로 뷰 '펑'하는 경우 감지하고
  • 다시 UI 프리젠 테이션 스타일에 따라 바인딩을 정리하려면이 옵션을 사용하여 시도 한 UI 프리젠 테이션 스타일 (모달, splitview,있는 navigationController, 팝업 등)에 따라, 우리는 바인딩 우리는 항상 우리가 검토 한
  • 바인딩을 정리하려고 여분의 장소로 폐기를 사용한 데이터 바인딩 모든 UIKit 클래스
  • 를 추가하고 정돈하는 ViewDidAppear, ViewDidDisappear 이벤트를 사용하여 시도했다 iOS/ObjC와 MonoTouch/.Net이 UIKit 객체를 소유하고있는 문제를 해결하기 위해 WeakReferences (특히 ViewModel에서 View로)를 사용합니다. 이러한 문제는 특히 어렵습니다. 디버깅 할 수 있습니다.
  • 약한 참조 코드는 다음 릴리스에서 핵심 변경 사항이 될 가능성이 큽니다.

(대신 터치보다 드로이드에서)이 약의 예 논의 미안 해요 https://github.com/slodge/MvvmCross/issues/17


에 나는이 순간에 당신에게 구체적인 조언을 제공 할 수있다. 바인딩을 생성하고 저장하는 방법에 대한 예제 코드를 게시하면 더 많은 도움을 줄 수 있습니다. 그러나 지금 만드는 바인딩을 실제로 시각화 할 수는 없습니다.

나는이 답변에 나중에 추가 할 링크가있어 - 현재 모바일에 - 너무 어렵다!


업데이트은 - 우리가 MvvmCross에 대한 v3에서 지금 향하고있는 곳 WeakReference를 아이디어에 좀 더 설명이이 - https://github.com/slodge/MvvmCross/tree/vNextDialog/Cirrious/Cirrious.MvvmCross.Binding/WeakSubscription - 기본적 아이디어는 일회용 WeakReference를 이벤트 구독을 사용하는 것입니다 - 어느하지 않습니다 UIKit 객체를 RAM에 보관하십시오. 아직 제대로 테스트되지 않은 코드입니다. 그것이 올 때, 나는 blog와 그것에 관해 더 완전하게 말할 것이다!

+0

감사합니다. 스튜어트! 몇 가지 예제 코드를 포함하도록 제 질문을 수정했습니다. 지금은 더 엄격한 ViewModel 라이프 사이클 접근 방식을 사용하려고합니다. 약한 위임 이벤트 접근법에 관해서는주의해서 진행하십시오. 우리는이 작업을 잠시 뒤늦게 수행했으며 모든 개발자가 머리 속에 보관해야하는 추가적인 복잡성입니다. 예 : 클로저를 사용하여 람다 구독으로 이러한 이벤트를 구독하면 메모리에 백업 클로저 인스턴스가 없으므로 버그를 추적하기가 매우 어려워지고 나중에 어떤 시점에서 GCed되어 이벤트 처리기를 무작위로 구독 취소합니다. – Tyson

+0

Thanks @Tyson - 최근 메신저 구현에 lambdas를 사용 해왔다. :) 약혼 한주의 (스레드가 8 개월 동안 열려있는 이유) – Stuart

+0

WeakReference stuff에 대한 상호 참조 추가 http://stackoverflow.com/ a/14734264/373321 – Stuart

관련 문제