2

ViewModels ライフサイクルで発生した問題について、@slodge のサンプルの 1 つを変更しました。

N26 を少し変更しました: https://github.com/csteeg/NPlus1DaysOfMvvmCross/tree/viewmodeldisposesample/N-26-Fraggle

このブランチでは、mvxmessenger プラグインを使用して、問題が発生した場所を表示できます。コードはきれいではありませんが、間違っていることを示しています。ビューがなくなっても、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 回など) 繰り返し続けると、一部のビューモデルがメッセージの受信を停止します (ガベージ コレクションが行われていると思います)。奇妙なことに、これらのビューの 1 つがHomeViewModelです。HomeView は決して破棄されませんが、homeviewmdoel はメッセージの受信を停止するため、アプリが必要とする場合に応じてビューを更新することはできません。

4

2 に答える 2

5

ビューにアタッチされなくなったビューモデルによって MvxMessages がまだ受信され、処理されているという同様の状況がありました。

私の解決策は、ベースビューモデルに以下を追加することです:

  • サブスクライブ メソッド
  • 登録解除アクションのリスト (これは Subscribe メソッドによって追加されます)
  • UnsubscribeAll メソッド

Android アクティビティの OnDestoy では、viewmodel の UnsubscribeAll を呼び出します。

(ボーナスとして、トークンは登録解除アクションで参照されるため、それらの別のリストを保持する必要はありません)

BaseViewModel:

    #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 に答える