我正在尝试使用MVVM模式构建我的应用程序。因此,我有ViewModel,它可以在数据发生变化时引发事件,并且预计UI会对这些事件作出反应并更新可见的UI控件。
我有一个派生UITableViewCell
,每次创建或出列新单元时都会使用某个ViewModel进行初始化(非常类似于miguel's example here)。一个主要区别是初始化的一部分依赖于订阅ViewModel的事件。这将创建从长期存在的ViewModel到此特定单元格的引用,将其保存在内存中以查看ViewModel的生命周期。重新使用单元格时,将清除旧订阅并为新ViewModel创建一个新订阅,该工作正常。
然而问题是,一旦单元格完全完成,似乎没有机会清理最后一个订阅,这意味着它在ViewModel的生命周期内被保存在内存中(比我想要的要长得多)是)。
“完全完成”取决于VC层次结构,但在这种情况下,包含具有自定义单元格的TableView的派生DialogViewController已从UINavigationController
堆栈中弹出并已被丢弃。
willMoveToSuperview
(我希望它会传入'null')。
永远不会调用removeFromSuperview
。
永远不会调用每个单元格。
处置UITableViewController不会处理每个单元格。
在控制器中处理TableView甚至不会处理每个单元格。
我可以手动处理每个单元格(从而清理订阅)的唯一方法是在我的每个派生UIViewControllers
中自己手动枚举单元格,这是我想要避免的。
有没有人有类似的麻烦?我不能成为第一个使用UVMableViewCells的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元素: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;
}
答案 0 :(得分:1)
你绝对不是第一个使用MonoTouch做Mvvm表格单元格的人。
我最近在http://slodge.blogspot.co.uk/2013/01/uitableviewcell-using-xib-editor.html
撰写了博客文章在此之前,在NDC有一些项目(搜索“挪威航班”),并且在MonoTouch.Dialog之上建立了一个长期运行的Mvvm项目。
在MvvmCross应用程序中,我们使用绑定到ObservableCollections的表和很多其他IList类。
在这些内容中,我们通常不会遇到左生命引用的许多问题,但这是因为我们通常不鼓励人们使用长寿命的ViewModel - 我们尝试创建一个新的ViewModel数据实例。视图。但是,我确实理解这可能并不适合所有的应用程序。
当Mvx用户发现自己遇到此类问题时,我们尝试过的一些方法是:
关于此问题的示例讨论(在Droid中而不是在触摸中)在https://github.com/slodge/MvvmCross/issues/17
上对不起,我现在不能给你任何具体的建议。如果你发布一些关于如何创建和存储绑定的示例代码,我可能会提供更多帮助 - 但我无法直观地看到你正在创建的绑定。
我还有一些链接,我将在稍后添加到此答案中 - 目前在移动设备上 - 在这里添加它们太难了!
更新 - 有关WeakReference想法的更多解释,这就是我们现在在v3中针对MvvmCross的问题 - https://github.com/slodge/MvvmCross/tree/vNextDialog/Cirrious/Cirrious.MvvmCross.Binding/WeakSubscription - 基本上我的想法是使用一次性弱引用事件订阅 - 它不会将UIKit对象保留在RAM中。它还没有经过适当测试的代码。如果是,那么我会博客并更全面地谈论它!