c# – 带有ObservableCollection的Windows Phone 8.1 WinRT内存
我正在处理在MapControl上显示的大量对象(PO??I).我正在帮助自己使用MVVM Light来遵守MVVM方法的规则.
由于我有义务在地图上显示每个对象,我必须使用MapItemsControl集合,而不是MapElements集合. XAML: <maps:MapControl x:Name="Map" x:Uid="MapControl"> <maps:MapItemsControl ItemsSource="{Binding Pushpins}"> <maps:MapItemsControl.ItemTemplate> <DataTemplate> <Image Source="{Binding Image}"/> </DataTemplate> </maps:MapItemsControl.ItemTemplate> </maps:MapItemsControl> MainViewModel: public class MainViewModel : ViewModelBase { public RelayCommand AddCommand { get; set; } public RelayCommand ClearCommand { get; set; } public RelayCommand CollectCommand { get; set; } public ObservableCollection<PushpinViewModel> Pushpins { get; set; } /* Ctor,initialization of Pushpins and stuff like that */ private void Collect() { GC.Collect(2); GC.WaitForPendingFinalizers(); GC.Collect(2); PrintCurrentMemory(); } private void Clear() { Pushpins.Clear(); PrintCurrentMemory(); } private void Add() { for (int i = 0; i < 1000; i++) { Pushpins.Add(new PushpinViewModel()); } PrintCurrentMemory(); } private void PrintCurrentMemory() { Logger.Log(String.Format("Total Memory: {0}",GC.GetTotalMemory(true) / 1024.0)); } } PushpinViewModel: public class PushpinViewModel: ViewModelBase { public string Image { get { return "/Assets/SomeImage.png"; } } ~PushpinViewModel() { Logger.Log("This finalizer never gets called!"); } } 现在,请考虑以下方案.我添加了Pushpins集合1000个PushpinViewModel元素.它们被渲染,内存被分配,一切都很好.现在我想清除集合,并添加另一个(在真实场景中不同)1000个元素.所以,我调用了Clear()方法.但是……没有任何反应!图钉被清除,但PushpinViewModel的终结器未被调用!然后我再次添加1000个元素,我的内存使用量翻倍. 那么,问题是什么?显然,ObservableCollection在对其执行了Clear()之后保持对PushpinViewModel对象的引用,因此它们不能被垃圾回收.当然,强制GC执行垃圾收集并没有帮助(有时甚至会使情况变得更糟). 现在困扰我2天了,我尝试了许多不同的场景来尝试克服这个问题,但说实话,没有任何帮助. 你有什么想法会导致这种内存泄漏吗?我如何强迫OC的成员摧毁?也许OC有某种替代方案? 编辑: 我使用XAML Map Control-https://xamlmapcontrol.codeplex.com/进行了一些测试,结果令人惊讶.添加了> 1000个元素的整体地图性能比原生MapControl差,但是,如果我调用Add()x1000,然后调用Clear(),然后添加()x1000,则PushpinViewModel的终结器调用!内存被释放,应用程序不会崩溃.所以微软的MapControl肯定有问题…… 解决方法
好的,这是我模仿MapItemsControl所做的行为.请注意,这是非常未经测试的 – 它适用于我的应用程序,但实际上并没有在其他任何地方尝试过.我从未测试过RemoveItems函数,因为我的应用程序只是将项添加到ObservableCollection并清除它们;它永远不会逐步删除项目.
另请注意,它使用绑定的项的哈希码标记XAML图钉;这是它如何识别集合更改时从地图中删除哪些图钉.这可能不适用于您的情况,但似乎是有效的. 用法: 注意:NumberedCircle是一个用户控件,它只是一个红色圆圈,在其中显示一个数字;替换为您想要用作图钉的任何XAML控件.目标是我的ObservableCollection对象,它具有Number属性(显示在图钉内)和Point属性(图钉位置). <map:MapControl> <i:Interaction.Behaviors> <behaviors:PushpinCollectionBehavior ItemsSource="{Binding Path=Destinations}"> <behaviors:PushpinCollectionBehavior.ItemTemplate> <DataTemplate> <controls:NumberedCircle Number="{Binding Path=Number}" map:MapControl.Location="{Binding Path=Point}" /> </DataTemplate> </behaviors:PushpinCollectionBehavior.ItemTemplate> </behaviors:PushpinCollectionBehavior> </i:Interaction.Behaviors> </map:MapControl> 码: using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.Linq; using System.Text; using System.Threading.Tasks; using Microsoft.Xaml.Interactivity; using Windows.Devices.Geolocation; using Windows.Foundation; using Windows.Storage.Streams; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls.Maps; namespace Foo.Behaviors { /// <summary> /// Behavior to draw pushpins on a map. This effectively replaces MapItemsControl,which is flaky as hell. /// </summary> public class PushpinCollectionBehavior : DependencyObject,IBehavior { #region IBehavior public DependencyObject AssociatedObject { get; private set; } public void Attach(Windows.UI.Xaml.DependencyObject associatedObject) { var mapControl = associatedObject as MapControl; if (mapControl == null) throw new ArgumentException("PushpinCollectionBehavior can be attached only to MapControl"); AssociatedObject = associatedObject; mapControl.Unloaded += MapControlUnloaded; } public void Detach() { var mapControl = AssociatedObject as MapControl; if (mapControl != null) mapControl.Unloaded -= MapControlUnloaded; } #endregion #region Dependency Properties /// <summary> /// The dependency property of the item that contains the pushpin locations. /// </summary> public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource",typeof(object),typeof(PushpinCollectionBehavior),new PropertyMetadata(null,OnItemsSourcePropertyChanged)); /// <summary> /// The item that contains the pushpin locations. /// </summary> public object ItemsSource { get { return GetValue(ItemsSourceProperty); } set { SetValue(ItemsSourceProperty,value); } } /// <summary> /// Adds,moves,or removes the pushpin when the item source changes. /// </summary> private static void OnItemsSourcePropertyChanged(DependencyObject dependencyObject,DependencyPropertyChangedEventArgs e) { var behavior = dependencyObject as PushpinCollectionBehavior; var mapControl = behavior.AssociatedObject as MapControl; // add the items if (behavior.ItemsSource is IList) behavior.AddItems(behavior.ItemsSource as IList); else throw new Exception("PushpinCollectionBehavior needs an IList as the items source."); // subscribe to changes in the collection if (behavior.ItemsSource is INotifyCollectionChanged) { var items = behavior.ItemsSource as INotifyCollectionChanged; items.CollectionChanged += behavior.CollectionChanged; } } // <summary> /// The dependency property of the pushpin template. /// </summary> public static readonly DependencyProperty ItemTemplateProperty = DependencyProperty.Register("ItemTemplate",typeof(DataTemplate),new PropertyMetadata(null)); /// <summary> /// The pushpin template. /// </summary> public DataTemplate ItemTemplate { get { return (DataTemplate)GetValue(ItemTemplateProperty); } set { SetValue(ItemTemplateProperty,value); } } #endregion #region Events /// <summary> /// Adds or removes the items on the map. /// </summary> private void CollectionChanged(object sender,NotifyCollectionChangedEventArgs e) { switch (e.Action) { case NotifyCollectionChangedAction.Add: AddItems(e.NewItems); break; case NotifyCollectionChangedAction.Remove: RemoveItems(e.OldItems); break; case NotifyCollectionChangedAction.Reset: ClearItems(); break; } } /// <summary> /// Removes the CollectionChanged event handler from the ItemsSource when the map is unloaded. /// </summary> void MapControlUnloaded(object sender,RoutedEventArgs e) { var items = ItemsSource as INotifyCollectionChanged; if (items != null) items.CollectionChanged -= CollectionChanged; } #endregion #region Private Functions /// <summary> /// Adds items to the map. /// </summary> private void AddItems(IList items) { var mapControl = AssociatedObject as MapControl; foreach (var item in items) { var templateInstance = ItemTemplate.LoadContent() as FrameworkElement; var hashCode = item.GetHashCode(); templateInstance.Tag = hashCode; templateInstance.DataContext = item; mapControl.Children.Add(templateInstance); Tags.Add(hashCode); } } /// <summary> /// Removes items from the map. /// </summary> private void RemoveItems(IList items) { var mapControl = AssociatedObject as MapControl; foreach (var item in items) { var hashCode = item.GetHashCode(); foreach (var child in mapControl.Children.Where(c => c is FrameworkElement)) { var frameworkElement = child as FrameworkElement; if (hashCode.Equals(frameworkElement.Tag)) { mapControl.Children.Remove(frameworkElement); continue; } } Tags.Remove(hashCode); } } /// <summary> /// Clears items from the map. /// </summary> private void ClearItems() { var mapControl = AssociatedObject as MapControl; foreach (var tag in Tags) { foreach (var child in mapControl.Children.Where(c => c is FrameworkElement)) { var frameworkElement = child as FrameworkElement; if (tag.Equals(frameworkElement.Tag)) { mapControl.Children.Remove(frameworkElement); continue; } } } Tags.Clear(); } #endregion #region Private Properties /// <summary> /// The object tags of the items this behavior has placed on the map. /// </summary> private List<int> Tags { get { if (_tags == null) _tags = new List<int>(); return _tags; } } private List<int> _tags; #endregion } } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |