1

MVVMパターンを使用してアプリケーションを構造化しようとしています。そのため、データが変更されたときにイベントを発生させるViewModelがあり、UIはそれらのイベントに反応して、表示されているUIコントロールを更新することが期待されています。

UITableViewCell新しいセルが作成またはデキューされるたびに特定のViewModelで初期化される派生物があります(ここでのmiguelの例と非常によく似ています)。初期化の一部である主な違いの1つは、ViewModelのイベントをサブスクライブすることに依存しています。これにより、長期間有効なViewModelからこの特定のセルへの参照が作成され、ViewModelの存続期間中はメモリに保持されます。セルが再利用されると、古いサブスクリプションがクリーンアップされ、新しいViewModelに新しいサブスクリプションが作成されます。これは正常に機能します。

ただし、問題は、セルが完全に終了すると最後のサブスクリプションをクリーンアップする機会がないように見えることです。つまり、セルはViewModelの存続期間中(私が望むよりもはるかに長く)メモリに保持されます。「完全に終了」はVC階層によって異なりますが、この場合、カスタムセルを含むTableViewを含む派生DialogViewControllerがUINavigationControllerスタックからポップされ、破棄されています。

willMoveToSuperview呼び出されることはありません(「null」が渡されることを期待していました)。 removeFromSuperview呼び出されることはありません。各セルの廃棄は呼び出されません。UITableViewControllerを破棄しても、各セルは破棄されません。コントローラ内でTableViewを破棄しても、各セルは破棄されません。

各セルを手動で破棄する(したがってサブスクリプションをクリーンアップする)ことができる唯一の方法は、派生した各セルでセルを手動で列挙するUIViewControllersことです。これは避けたいことです。

誰かがこのような同様の問題を抱えていますか?UITableViewCellsでMVVMパターンを最初に使用することはできません。これは、ベースのMonoTouch UIKitラッパーのDisposeパターンのバグですか?

編集:これはカスタムの1つの縮小版ですUITableViewCells。変更される可能性があることがわかっているプロパティのイベントを明示的にサブスクライブする実用的なアプローチを採用していることに注意してください。完全なMVVMがすべてのプロパティをUIにバインドするわけではありません。したがって、私のバインディングコードは、標準のイベントサブスクリプションのみで構成されています。

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のelement:viewmodelがあるため、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;
    }
4

1 に答える 1

1

MonoTouch で Mvvm テーブル セルを実行するのは、あなたが初めてではありません。

最近、http: //slodge.blogspot.co.uk/2013/01/uitableviewcell-using-xib-editor.html でブログを書いています。

その前に、NDC ("Flights of Norway" で検索) にプロジェクトがあり、MonoTouch.Dialog の上に構築された長期にわたる Mvvm プロジェクトがありました。


MvvmCross アプリでは、ObservableCollections や他の IList クラスにバインドされたテーブルを頻繁に使用します。

これらの範囲内では、通常、レフトリビング参照で多くの問題に遭遇することはありませんが、それは、一般に、ユーザーが長期の ViewModel を使用することを推奨していないためです。各ビューに合わせて ViewModel データの新しいインスタンスを作成しようとします。ただし、それがすべてのアプリケーションに適しているとは限らないことを理解しています。


Mvx ユーザーがこの種の問題に遭遇した場合、私たちが試したアプローチのいくつかは次のとおりです。

  • iOS5 では ViewDidUnload メソッドを使用してバインドをクリーンアップしましたが、iOS6 では明らかにそれがなくなりました。
  • UI 表示スタイル (モーダル、スプリットビュー、ナビゲーション コントローラー、ポップアップなど) に応じて、ビューが「ポップ」されるタイミングを手動で検出し、これを使用してバインディングをクリアしようとしました。
  • 再び UI 表示スタイルに応じて、ViewDidAppear、ViewDidDisappear イベントを使用してバインディングを追加および整理しようとしました
  • データ バインディングを使用するすべての UIKit クラスでは、バインディングをクリアするための追加の場所として常に Dispose を使用してきました。
  • UIKit オブジェクトが iOS/ObjC と MonoTouch/.Net の両方によって所有されているという問題を回避するために、WeakReferences (特に ViewModel から View へ) の使用を検討しました。これらの問題は特にデバッグが困難です。
  • この弱い参照コードは、次のリリースでの重要な変更の 1 つになる可能性があります。

これに関する議論の例 (Touch ではなく Droid で) は、https://github.com/slodge/MvvmCross/issues/17にあります。


申し訳ありませんが、現時点では具体的なアドバイスを提供できません。バインディングの作成方法と保存方法についてさらにサンプル コードを投稿していただければ、さらにお役に立てるかもしれませんが、現在作成しているバインディングを実際に視覚化することはできません。

後でこの回答に追加するリンクがいくつかありますが、現時点ではモバイル上で、ここに追加するのは難しすぎます。


更新- WeakReference のアイデアの詳細については、MvvmCross の v3 で現在向かっているところです - https://github.com/slodge/MvvmCross/tree/vNextDialog/Cirrious/Cirrious.MvvmCross.Binding/WeakSubscription -基本的には、UIKit オブジェクトを RAM に保持しない、使い捨ての weakreference イベント サブスクリプションを使用するという考え方です。まだ適切にテストされたコードではありません。そのときは、ブログを書いて、それについてもっと詳しく話します!

于 2013-02-23T10:35:40.753 に答える