1

我正在尝试使用 MVVM 模式来构建我的应用程序。因此,我有 ViewModels 在数据更改时引发事件,并且 UI 应该对这些事件做出反应并更新可见的 UI 控件。

我有一个派生UITableViewCell,每次创建或出列新单元格时都会使用某个 ViewModel 进行初始化(与miguel 的示例非常相似)。初始化的一个主要区别在于订阅 ViewModel 的事件。这会创建一个从长期存在的 ViewModel 到这个特定单元的引用,并在 ViewModel 的生命周期内将其保存在内存中。当单元被重新使用时,旧的订阅被清理并为新的 ViewModel 创建一个新的订阅,这工作正常。

然而问题是,一旦单元格完全完成,似乎没有任何机会清理最后一个订阅,这意味着它在 ViewModel 的生命周期内保存在内存中(比我想要的要长得多)。“完全完成”取决于 VC 层次结构,但在这种情况下,包含具有自定义单元格的 TableView 的派生 DialogViewController 已从UINavigationController堆栈中弹出并已被处置。

willMoveToSuperview永远不会被调用(我希望它会传入'null')。 removeFromSuperview永远不会被调用。永远不会调用每个单元格上的 Dispose。处理 UITableViewController 不会处理每个单元格。在控制器中处理 TableView 甚至不会处理每个单元格。

我可以手动处理每个单元格(并因此清理订阅)的唯一方法是自己手动枚举每个派生单元中的单元格UIViewControllers,这是我想避免的。

有没有人遇到过类似的烦恼?我不能成为第一个使用带有 UITableViewCells 的 MVVM 模式的人。这是基本 MonoTouch UIKit 包装器中的 Dispose 模式的错误吗?

编辑:这是自定义之一的精简版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 数据实例以与每个 View 一起使用。但是,我明白这可能并不适合所有应用程序。


当 Mvx 用户发现自己遇到此类问题时,我们尝试过的一些方法是:

  • 在 iOS5 中,我们确实使用了 ViewDidUnload 方法来清理绑定——但显然现在 iOS6 中已经没有了。
  • 根据 UI 呈现风格(模态、拆分视图、导航控制器、弹出窗口等),我们尝试手动检测视图何时“弹出”并使用它来清除绑定
  • 再次根据 UI 呈现风格,我们尝试使用 ViewDidAppear、ViewDidDisappear 事件来添加和整理绑定
  • 对于所有具有数据绑定的 UIKit 类,我们一直使用 Dispose 作为尝试清除绑定的额外位置
  • 我们已经研究过使用 Wea​​kReferences(尤其是从 ViewModel 到 View)来解决 UIKit 对象同时属于 iOS/ObjC 和 MonoTouch/.Net 的问题——这些问题特别难以调试。
  • 这个弱参考代码很可能是下一个版本的关键变化之一。

有关此问题的示例讨论(在 Droid 中而不是在 Touch 中)位于https://github.com/slodge/MvvmCross/issues/17


很抱歉,我目前无法为您提供任何具体建议。如果您发布更多有关如何创建和存储绑定的示例代码,我可能会提供更多帮助 - 但我无法真正想象您现在正在创建哪些绑定。

我还有一些链接,稍后我会添加到这个答案中 - 目前在移动设备上 - 很难在此处添加它们!


更新- 有关 WeakReference 想法的更多解释,这是我们现在在 v3 中为 MvvmCross 前进的方向 - https://github.com/slodge/MvvmCross/tree/vNextDialog/Cirrious/Cirrious.MvvmCross.Binding/WeakSubscription -基本上这个想法是使用一次性弱引用事件订阅 - 它不会将 UIKit 对象保留在 RAM 中。它还没有经过适当测试的代码。到时候,我会在博客上更全面地谈论它!

于 2013-02-23T10:35:40.753 回答