如何管理MVVM中的对象生存期?

时间:2013-09-12 07:21:42

标签: c# wpf mvvm

我正在研究使用C#和WPF构建的应用程序,它(很糟糕地)实现了MVVM。工作分解如下:

  • 查看

    • 演示文稿,位图,颜色等
    • 动画(如果使用)
    • 通过绑定数据与ViewModel进行通信
    • 通过在其上添加命令与ViewModel进行通信
  • 视图模型

    • 公开要由视图调用的命令
    • 将数据处理功能委托给模型
    • 定义UI行为
    • 对视图没有直接(命名)依赖
    • 通过调用模型中的方法与模型进行通信
    • 可以通过订阅模型公开的事件通知模型中的更改
  • 模型

    • 磁盘持久性,数据分析等
    • 其他所有
    • 不依赖于ViewModel

不幸的是,这导致了循环引用,因为视图需要引用视图模型来引发事件和激活命令,视图模型需要引用视图才能更新视图状态(通常这个引用是由视图模型为WPF的{ {1}}),viewmodels需要引用模型来委派工作,而模型通常需要通知视图模型一些外部状态变化。

所以我们有两个循环引用问题,viewmodel位于中间。因此,此应用程序遇到内存消耗问题,因为WPF实体正在创建并与模型中的某些数据相关联,并且这些实体永远不会被清除(直到程序终止)。

这应该如何处理?

似乎需要定义所有权图,这样一个或多个这些组件负责在事件处理程序不再相关时断开事件处理程序,以便事物可以进行GC。

3 个答案:

答案 0 :(得分:1)

您的问题没有提供有关实例化内容的足够信息,我怀疑如果不挖掘所有代码就可以解决这个问题。

首先:这是一个非常糟糕的MVVM,因为这里违反了层和关注点的分离。

我建议先解决这个问题。 你的问题可能是对XAML和实例化的理解很差(没有冒犯)

在不触及架构的情况下,您需要对应用程序进行概要分析,以查看哪些对象被实例化了多少次(内存热点)。看看哪些GC生成这些对象可能也很有趣,因为这可能表示一些严重的(手动)内存管理问题。

事件处理程序往往会造成泄漏。因此,可能会应用弱参考模式:http://msdn.microsoft.com/en-us/library/aa970850.aspx

一个好的MVVM有一些生命周期管理,也可以作为DI和IoC容器服务器,通过它来处理复杂的生命周期场景

答案 1 :(得分:1)

好的,我以为你已经直接使用了所有这些

如果您没有任何缓存视图,那么为什么您的内存使用率很高?

MVVM应用程序中不存在内存问题(即使是非常好的架构应用程序) 你有什么静态物品?

您是否曾使用此代码调查wpf应用程序绑定的当前情况?

private static IList<BindingInfo> getReflectPropertyDescriptorInfo()
        {
            var results = new List<BindingInfo>();

            var reflectTypeDescriptionProvider = typeof(PropertyDescriptor).Module.GetType("System.ComponentModel.ReflectTypeDescriptionProvider");
            var propertyCacheField = reflectTypeDescriptionProvider.GetField("_propertyCache",
                BindingFlags.Static | BindingFlags.NonPublic);
            if (propertyCacheField == null)
                throw new NullReferenceException("`ReflectTypeDescriptionProvider._propertyCache` not found");

            var propertyCacheItems = propertyCacheField.GetValue(null) as Hashtable;
            if (propertyCacheItems == null)
                return results;

            var valueChangedHandlersField = typeof(PropertyDescriptor).GetField("valueChangedHandlers",
                BindingFlags.Instance | BindingFlags.NonPublic);

            if (valueChangedHandlersField == null)
                return results;

            foreach (DictionaryEntry entry in propertyCacheItems)
            {
                var properties = entry.Value as PropertyDescriptor[];
                if (properties == null)
                    continue;

                foreach (var property in properties)
                {
                    var valueChangedHandlers = valueChangedHandlersField.GetValue(property) as Hashtable;
                    if (valueChangedHandlers != null && valueChangedHandlers.Count != 0)
                        results.Add(new BindingInfo
                            {
                                TypeName = entry.Key.ToString(),
                                PropertyName = property.Name,
                                HandlerCount = valueChangedHandlers.Count
                            });
                }
            }

            return results;
        }

使用此代码,您可以找到内存中的绑定?

答案 2 :(得分:0)

“viewmodels需要引用视图才能更新视图状态(通常这个引用是由一个视图模型作为WPF的DataContext),” - 此时没有依赖,因为View不知道DataContext和ViewModel中的内容是什么并不是真的了解View,所以它并不是View的ViewModel中的devependency。

当您需要将ViewModel分配给DataContext时,您有依赖关系,并且View依赖于ViewModel(它不需要因为可以使用IViewModel而不是ViewModel)

即使使用ViewModel第一种方法,您实际上没有View,也有View实现的IView合约。

class ContentViewModel{


 IView view;

 public ContentViewModel(IContentAView view)
       {
            View = view;
            View.ViewModel = this;

如果您使用容器作为Unity并且视图模型或视图每次都被实例化,但旧实例不是GC因为某些引用(可能是事件处理程序)而导致内存泄漏。您需要分析内存并找出创建新实例的时间,并使用某些工具(如Microsoft WinDbg的RedGate内存分析器)查找对旧实例的引用。