4

我正在开发一个使用 C# 和 WPF 构建的应用程序,它(严重)实现了 MVVM。工作分解是这样的:

  • 看法

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

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

    • 磁盘持久化、数据分析等
    • 其他一切
    • 不依赖于 ViewModel

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

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

这个应该怎么处理?

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

4

3 回答 3

1

您的问题没有提供有关实例化内容的足够信息,我怀疑是否有可能在不深入研究所有代码的情况下解决此问题。

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

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

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

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

一个好的 MVVM 具有某种生命周期管理,它也作为 DI 和 IoC 容器服务,通过它们应该处理复杂的生命周期场景

于 2013-09-12T07:27:21.367 回答
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;
        }

使用此代码,您可以找出内存中的绑定是什么?

于 2013-09-12T07:43:55.010 回答
0

“视图模型需要对视图的引用才能更新视图状态(通常这个引用是由作为 WPF 的 DataContext 的视图模型进行的),” - 此时没有依赖关系,因为 View 不知道 DataContext 中的内容,而 ViewModel 并不真正知道关于 View 的任何事情,所以它肯定不是 View 的 ViewModel 的依赖关系。

当您需要将 ViewModel 分配给 DataContext 时,您得到了依赖,而 View 依赖于 ViewModel(它不需要,因为可以使用 IViewModel 代替 ViewModel)

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

class ContentViewModel{


 IView view;

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

如果您使用容器作为 Unity 并且每次都实例化 View Model 或 View 但旧实例不是 GC,因为某些引用(可能是事件处理程序)您将有内存泄漏。您需要使用 Microsoft WinDbg 的 RedGate memory profiler 等工具来分析内存并找出新实例何时创建并查找对旧实例的引用。

于 2013-09-12T08:32:50.090 回答