From 93397893e56cf30c4270a8c4e9964267b536a5aa Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Thu, 23 Feb 2023 11:07:34 -0600 Subject: [PATCH] [windows] fix memory leak when `CollectionView.ItemsSource` changes Fixes: https://github.com/dotnet/maui/issues/13393 Context: https://github.com/ivan-todorov-progress/maui-collection-view-memory-leak-bug Debugging the above sample, I found that `ItemsView._logicalChildren` grew indefinitely in size if you replace a `CollectionView`'s `ItemsSource` over and over. I could fix this by creating a new `internal` `ItemsView.ClearLogicalChildren()` method and call it from the Windows `ItemsViewHandler`. WIP to determine if we can write a test or if this happens on any other platforms. Android seems OK. --- .../src/Core/Handlers/Items/ItemsViewHandler.Windows.cs | 1 + src/Controls/src/Core/Items/ItemsView.cs | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/src/Controls/src/Core/Handlers/Items/ItemsViewHandler.Windows.cs b/src/Controls/src/Core/Handlers/Items/ItemsViewHandler.Windows.cs index 0a9116775412..c0b8a55bd9d1 100644 --- a/src/Controls/src/Core/Handlers/Items/ItemsViewHandler.Windows.cs +++ b/src/Controls/src/Core/Handlers/Items/ItemsViewHandler.Windows.cs @@ -123,6 +123,7 @@ protected virtual void CleanUpCollectionViewSource() CollectionViewSource.Source = null; CollectionViewSource = null; } + VirtualView?.ClearLogicalChildren(); if (VirtualView?.ItemsSource == null) { diff --git a/src/Controls/src/Core/Items/ItemsView.cs b/src/Controls/src/Core/Items/ItemsView.cs index 3cced27c4eaf..8e6a56ca0233 100644 --- a/src/Controls/src/Core/Items/ItemsView.cs +++ b/src/Controls/src/Core/Items/ItemsView.cs @@ -142,6 +142,15 @@ public void RemoveLogicalChild(Element element) VisualDiagnostics.OnChildRemoved(this, element, oldLogicalIndex); } + internal void ClearLogicalChildren() + { + // Reverse for-loop, so children can be removed while iterating + for (int i = _logicalChildren.Count - 1; i >= 0; i--) + { + RemoveLogicalChild(_logicalChildren[i]); + } + } + internal override IReadOnlyList LogicalChildrenInternal => _logicalChildren.AsReadOnly(); internal static readonly BindableProperty InternalItemsLayoutProperty =