2

我已经针对我的 ViewModels 生命周期中的问题修改了@slodge 的一个示例。

我对 N26 做了一点修改: https ://github.com/csteeg/NPlus1DaysOfMvvmCross/tree/viewmodeldisposesample/N-26-Fraggle

这个分支使用 mvxmessenger 插件能够告诉你哪里出了问题。代码不是很漂亮,但表明你 wat 不正确。您可以看到 ID = 0 的 SubViewModel 如何继续接收消息,即使它的视图早已不复存在。还有(在某些时候) HomeViewModel 如何停止接收消息。

重现步骤(包括调试输出的清理版本):

启动应用程序

HomeViewModel:Warning:HomeViewModel 0 received: Created HomeViewModel0

点击按钮“1”

HomeViewModel:Warning:HomeViewModel 0 received: Created SubViewModel0 SubViewModel:Warning:SubViewModel 0 received: Created SubViewModel0 HomeViewModel:Warning:HomeViewModel 0 received: Created FirstViewModel0 SubViewModel:Warning:SubViewModel 0 received: Created FirstViewModel0 FirstViewModel:Warning:FirstViewModel 0 received: Created FirstViewModel0

点击返回

HomeViewModel:Warning:HomeViewModel 0 received: Destroyed FirstView for viewmodel 0 SubViewModel:Warning:SubViewModel 0 received: Destroyed FirstView for viewmodel 0 FirstViewModel:Warning:FirstViewModel 0 received: Destroyed FirstView for viewmodel 0 HomeViewModel:Warning:HomeViewModel 0 received: Destroyed SubFrag for viewmodel 0 SubViewModel:Warning:SubViewModel 0 received: Destroyed SubFrag for viewmodel 0 FirstViewModel:Warning:FirstViewModel 0 received: Destroyed SubFrag for viewmodel 0 HomeViewModel:Warning:HomeViewModel 0 received: Destroyed DubFrag for viewmodel 0 SubViewModel:Warning:SubViewModel 0 received: Destroyed DubFrag for viewmodel 0 FirstViewModel:Warning:FirstViewModel 0 received: Destroyed DubFrag for viewmodel 0 您可以在这里看到视图被破坏,我希望视图模型与它们一起使用

再次单击按钮“1”

HomeViewModel:Warning:HomeViewModel 0 received: Created SubViewModel1 SubViewModel:Warning:SubViewModel 0 received: Created SubViewModel1 FirstViewModel:Warning:FirstViewModel 0 received: Created SubViewModel1 SubViewModel:Warning:SubViewModel 1 received: Created SubViewModel1 HomeViewModel:Warning:HomeViewModel 0 received: Created FirstViewModel1 SubViewModel:Warning:SubViewModel 0 received: Created FirstViewModel1 FirstViewModel:Warning:FirstViewModel 0 received: Created FirstViewModel1 SubViewModel:Warning:SubViewModel 1 received: Created FirstViewModel1 FirstViewModel:Warning:FirstViewModel 1 received: Created FirstViewModel1

你看,subviewmodel 0 仍在接收消息。我可以以某种方式告诉它应该停止向未附加的视图模型发送消息吗?或者视图模型是否知道未附加

现在,当您继续重复这些步骤相当长的一段时间,比如在模拟器中 15 次时,一些视图模型将停止接收消息(我猜它们是垃圾收集的)。奇怪的是,其中一个视图是HomeViewModel!HomeView 永远不会被破坏,但是 homeviewmdoel 会停止接收消息,因此如果您的应用程序需要,nog 能够相应地更新视图

4

2 回答 2

5

我遇到了类似的情况,MvxMessages 仍然被不再附加到视图的视图模型接收和操作。

我的解决方案是将以下内容添加到基本视图模型中:

  • 订阅方法
  • 取消订阅操作列表(由订阅方法添加)
  • UnsubscribeAll 方法

在 Android 活动 OnDestoy 中,我调用了 viewmodel 的 UnsubscribeAll。

(作为奖励,由于在取消订阅操作中引用了令牌,我不需要保留另一个列表)

基础视图模型:

    #region Messenger

    /// <summary>
    /// Must set the Messenger object before doing any subscribing
    /// </summary>
    public IMvxMessenger Messenger { get; set; }

    private readonly object _messengerLock = new Object();

    private List<Action> _unsubscribeActions;

    /// <summary>
    /// Subscribe to a message, and store in a list so can be unsubscribed automatically later
    /// </summary>
    /// <typeparam name="TMessage"></typeparam>
    /// <param name="deliveryAction"></param>
    public void Subscribe<TMessage>(Action<TMessage> deliveryAction) where TMessage: MvxMessage
    {
        var messenger = Messenger;
        if (messenger == null) { return; }

        var token = messenger.Subscribe<TMessage>(deliveryAction);

        Action unsubscriber = delegate()
        {
            messenger.Unsubscribe<TMessage>(token);
        };

        lock (_messengerLock)
        {
            if (_unsubscribeActions == null)
            {
                _unsubscribeActions = new List<Action>();
            }
            _unsubscribeActions.Add(unsubscriber);
        }
    }

    /// <summary>
    /// Unsubscribe to all messages which have been previously subscribed to
    /// </summary>
    public void UnsubscribeAll()
    {
        if (_unsubscribeActions == null) { return; }
        lock (_messengerLock)
        {
            foreach (var a in _unsubscribeActions)
            {
                a();
            }
            _unsubscribeActions = null;
        }
    }

    #endregion

基本活动:

    protected override void OnDestroy()
    {
        var vm = ViewModel as ViewModel.BaseViewModel;
        if (vm != null) { vm.UnsubscribeAll(); }
        base.OnDestroy();
    }
于 2013-10-22T14:46:43.703 回答
4

MvvmCross v3 不会将 ViewModel 暴露给任何 View 生命周期事件,例如 ViewDidAppear/Disappear、OnNavigatedTo/From、OnPause/OnResume/OnDestroy。

这样做的原因是:

  • 早期的 MvvmCross 版本试图做到这一点,但由于 iOS 的变化ViewDidUnload和一般的 iOS 支持而陷入困境(当视图永远死掉时很难解决)
  • 当视图不是页面时——而是选项卡、弹出窗口、拆分视图、弹出窗口等——那么它很难支持并且让开发人员感到困惑

相反,MvvmCross v3 使用垃圾收集来整理 ViewModel。为了协助这个 Mvx 总是使用WeakReferenceViewModel-to-View 和 MessageHub-to-ViewModels 的 s。遵循的一般哲学是:

  • 唯一在视图上保持强引用的是操作系统。
  • 唯一在 ViewModel 上保持强引用的是它的 View。

话虽如此,如果 GarbageCollection 对您的应用程序来说不够及时,那么 MvvmCross 确实允许您ViewModel使用新功能扩展您的 s。例如,您可以根据需要轻松添加新IViewLifecycleAware界面。这很容易做到,但一旦完成,您的应用就有责任确保从您支持的每个平台上的适当视图事件/覆盖调用接口。

关于这个主题还有更多内容:


对于您HomeViewModel停止接收消息的具体问题,我认为这是由于您没有存储订阅令牌。

因为 MvvmCross 信使默认使用弱引用,所以您必须存储订阅令牌 - 当该令牌被释放或垃圾收集时,订阅将被取消订阅。

所以你的代码:

public class HomeViewModel
    : MvxViewModel
{
    public static int IdCounter = 0;
    public int Id = IdCounter++;
    public HomeViewModel()
    {

        var messenger = Mvx.Resolve<IMvxMessenger>();
        messenger.Subscribe<JustAMessage>(OnMessage);
        messenger.Publish(new JustAMessage(this) { Message = "Created HomeViewModel" + Id });
    }

    // ...

需要是:

public class HomeViewModel
    : MvxViewModel
{
    public static int IdCounter = 0;
    public int Id = IdCounter++;

    private IDisposable _token;

    public HomeViewModel()
    {

        var messenger = Mvx.Resolve<IMvxMessenger>();
        _token = messenger.Subscribe<JustAMessage>(OnMessage);
        messenger.Publish(new JustAMessage(this) { Message = "Created HomeViewModel" + Id });
    }

    // ...

关于这一点的更多信息:

于 2013-09-25T14:44:24.637 回答