Skip to content

Commit 7bacbce

Browse files
authored
[Shell] Fixed memory leak when Shell items change where child component handlers were not disconnected on iOS, and neither page nor child handlers were disconnected on Android (#840)
1 parent 965efb4 commit 7bacbce

11 files changed

Lines changed: 174 additions & 154 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## [55.6.8]
2+
- [Shell] Fixed memory leak when Shell items change where child component handlers were not disconnected on iOS, and neither page nor child handlers were disconnected on Android
3+
- [MemoryManagement] Fixed false-positive memory leak reports when BindingContext is a string
4+
15
## [55.6.7]
26
- Fixed memory leak where modal pages with `ToolbarItems` were retained after dismissal when using `x:Name` on any element in the page or `Clicked` event on the `ToolbarItem`.
37

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
using DIPS.Mobile.UI.Components.ListItems;
2+
using DIPS.Mobile.UI.Components.ListItems.Extensions;
3+
4+
namespace MemoryLeakTests.Tests;
5+
6+
public class ShellItemChangedTests : UITest
7+
{
8+
public override void BeforeTest(ContentPage contentPage)
9+
{
10+
contentPage.Content = new ListItem
11+
{
12+
Title = "Swap to multi-tab shell",
13+
Command = new Command(SwapToMultiTabShell)
14+
};
15+
}
16+
17+
public override string Name => "Shell Item Changed";
18+
19+
private static void SwapToMultiTabShell()
20+
{
21+
var tabBar = new TabBar();
22+
23+
var tab1 = new Tab { Title = "Tab 1" };
24+
tab1.Items.Add(new ShellContent
25+
{
26+
ContentTemplate = new DataTemplate(() => new NavigablePage("Tab 1"))
27+
});
28+
29+
var tab2 = new Tab { Title = "Tab 2" };
30+
tab2.Items.Add(new ShellContent
31+
{
32+
ContentTemplate = new DataTemplate(() => new NavigablePage("Tab 2"))
33+
});
34+
35+
tabBar.Items.Add(tab1);
36+
tabBar.Items.Add(tab2);
37+
Shell.Current.Items.Clear();
38+
Shell.Current.Items.Add(tabBar);
39+
}
40+
41+
internal static void SwapBackToMainPage()
42+
{
43+
var tabBar = new TabBar();
44+
var tab = new Tab();
45+
tab.Items.Add(new ShellContent
46+
{
47+
ContentTemplate = new DataTemplate(() => App.Container.GetInstance(typeof(MainPage)))
48+
});
49+
tabBar.Items.Add(tab);
50+
Shell.Current.Items.Clear();
51+
Shell.Current.Items.Add(tabBar);
52+
}
53+
54+
private class NavigablePage : ContentPage
55+
{
56+
public NavigablePage(string tabName)
57+
{
58+
Title = tabName;
59+
60+
var layout = new VerticalStackLayout { Spacing = 0 };
61+
62+
layout.Add(new NavigationListItem
63+
{
64+
Title = "Push page",
65+
Command = new Command(() =>
66+
Shell.Current.Navigation.PushAsync(new NavigablePage(tabName)))
67+
});
68+
69+
layout.Add(new ListItem
70+
{
71+
Title = "Switch back to tests",
72+
Command = new Command(SwapBackToMainPage)
73+
});
74+
75+
Content = layout;
76+
}
77+
}
78+
}

src/app/MemoryLeakTests/Tests/TestRegistrator.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ public static void Register(ServiceContainer serviceContainer)
1010
serviceContainer.Register<CollectionViewTests>()
1111
.Register<ModalTests>()
1212
.Register<SimpleModalTests>()
13-
.Register<NavigationTests>();
13+
.Register<NavigationTests>()
14+
.Register<ShellItemChangedTests>();
1415

1516
// Component tests
1617
serviceContainer.Register<EntryTest>()

src/app/Playground/TestTab.xaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
<dui:ListItem Tapped="ListItem_OnTapped2"></dui:ListItem>
2020

21+
<dui:SegmentedControl />
22+
2123
</VerticalStackLayout>
2224

2325
</dui:ContentPage>

src/app/Playground/TestTab.xaml.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,9 @@ private void ListItem_OnTapped2(object sender, EventArgs e)
2929
{
3030
Navigation.PushAsync(new VetlePage());
3131
}
32+
33+
protected override void OnHandlerChanged()
34+
{
35+
base.OnHandlerChanged();
36+
}
3237
}

src/app/Playground/VetleSamples/VetlePage.xaml

Lines changed: 1 addition & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -16,151 +16,7 @@
1616
ColumnSpacing="0"
1717
RowSpacing="0">
1818

19-
<!-- Vertical scroll (left column - like Antibiotics) -->
20-
<dui:ScrollView x:Name="VerticalScroll"
21-
Orientation="Vertical"
22-
VerticalScrollBarVisibility="Never"
23-
ShouldBounce="True"
24-
Grid.Row="1">
25-
<dui:VerticalStackLayout Spacing="0">
26-
<BoxView HeightRequest="60" WidthRequest="120" Color="LightBlue" />
27-
<BoxView HeightRequest="60" WidthRequest="120" Color="LightGreen" />
28-
<BoxView HeightRequest="60" WidthRequest="120" Color="LightBlue" />
29-
<BoxView HeightRequest="60" WidthRequest="120" Color="LightGreen" />
30-
<BoxView HeightRequest="60" WidthRequest="120" Color="LightBlue" />
31-
<BoxView HeightRequest="60" WidthRequest="120" Color="LightGreen" />
32-
<BoxView HeightRequest="60" WidthRequest="120" Color="LightBlue" />
33-
<BoxView HeightRequest="60" WidthRequest="120" Color="LightGreen" />
34-
<BoxView HeightRequest="60" WidthRequest="120" Color="LightBlue" />
35-
<BoxView HeightRequest="60" WidthRequest="120" Color="LightGreen" />
36-
<BoxView HeightRequest="60" WidthRequest="120" Color="LightBlue" />
37-
<BoxView HeightRequest="60" WidthRequest="120" Color="LightGreen" />
38-
<BoxView HeightRequest="60" WidthRequest="120" Color="LightBlue" />
39-
<BoxView HeightRequest="60" WidthRequest="120" Color="LightGreen" />
40-
<BoxView HeightRequest="60" WidthRequest="120" Color="LightBlue" />
41-
<BoxView HeightRequest="60" WidthRequest="120" Color="LightGreen" />
42-
</dui:VerticalStackLayout>
43-
</dui:ScrollView>
44-
45-
<!-- Horizontal scroll (top row - like Findings header) -->
46-
<dui:ScrollView x:Name="HorizontalScroll"
47-
Orientation="Horizontal"
48-
HorizontalScrollBarVisibility="Never"
49-
ShouldBounce="False"
50-
Grid.Column="1">
51-
<dui:HorizontalStackLayout Spacing="0">
52-
<BoxView HeightRequest="40" WidthRequest="80" Color="LightCoral" />
53-
<BoxView HeightRequest="40" WidthRequest="80" Color="LightSalmon" />
54-
<BoxView HeightRequest="40" WidthRequest="80" Color="LightCoral" />
55-
<BoxView HeightRequest="40" WidthRequest="80" Color="LightSalmon" />
56-
<BoxView HeightRequest="40" WidthRequest="80" Color="LightCoral" />
57-
<BoxView HeightRequest="40" WidthRequest="80" Color="LightSalmon" />
58-
<BoxView HeightRequest="40" WidthRequest="80" Color="LightCoral" />
59-
<BoxView HeightRequest="40" WidthRequest="80" Color="LightSalmon" />
60-
<BoxView HeightRequest="40" WidthRequest="80" Color="LightCoral" />
61-
<BoxView HeightRequest="40" WidthRequest="80" Color="LightSalmon" />
62-
</dui:HorizontalStackLayout>
63-
</dui:ScrollView>
64-
65-
<!-- Bidirectional scroll (main matrix area) - this is where ShouldBounce breaks -->
66-
<dui:ScrollView x:Name="BidirectionalScrollView"
67-
Orientation="Both"
68-
ShouldBounce="False"
69-
Grid.Row="1"
70-
Grid.Column="1">
71-
<dui:HorizontalStackLayout Spacing="0">
72-
<dui:VerticalStackLayout Spacing="0">
73-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightGoldenrodYellow" />
74-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightYellow" />
75-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightGoldenrodYellow" />
76-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightYellow" />
77-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightGoldenrodYellow" />
78-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightYellow" />
79-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightGoldenrodYellow" />
80-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightYellow" />
81-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightGoldenrodYellow" />
82-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightYellow" />
83-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightGoldenrodYellow" />
84-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightYellow" />
85-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightGoldenrodYellow" />
86-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightYellow" />
87-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightGoldenrodYellow" />
88-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightYellow" />
89-
</dui:VerticalStackLayout>
90-
<dui:VerticalStackLayout Spacing="0">
91-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightYellow" />
92-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightGoldenrodYellow" />
93-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightYellow" />
94-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightGoldenrodYellow" />
95-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightYellow" />
96-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightGoldenrodYellow" />
97-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightYellow" />
98-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightGoldenrodYellow" />
99-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightYellow" />
100-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightGoldenrodYellow" />
101-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightYellow" />
102-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightGoldenrodYellow" />
103-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightYellow" />
104-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightGoldenrodYellow" />
105-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightYellow" />
106-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightGoldenrodYellow" />
107-
</dui:VerticalStackLayout>
108-
<dui:VerticalStackLayout Spacing="0">
109-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightGoldenrodYellow" />
110-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightYellow" />
111-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightGoldenrodYellow" />
112-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightYellow" />
113-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightGoldenrodYellow" />
114-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightYellow" />
115-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightGoldenrodYellow" />
116-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightYellow" />
117-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightGoldenrodYellow" />
118-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightYellow" />
119-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightGoldenrodYellow" />
120-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightYellow" />
121-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightGoldenrodYellow" />
122-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightYellow" />
123-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightGoldenrodYellow" />
124-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightYellow" />
125-
</dui:VerticalStackLayout>
126-
<dui:VerticalStackLayout Spacing="0">
127-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightYellow" />
128-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightGoldenrodYellow" />
129-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightYellow" />
130-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightGoldenrodYellow" />
131-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightYellow" />
132-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightGoldenrodYellow" />
133-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightYellow" />
134-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightGoldenrodYellow" />
135-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightYellow" />
136-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightGoldenrodYellow" />
137-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightYellow" />
138-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightGoldenrodYellow" />
139-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightYellow" />
140-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightGoldenrodYellow" />
141-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightYellow" />
142-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightGoldenrodYellow" />
143-
</dui:VerticalStackLayout>
144-
<dui:VerticalStackLayout Spacing="0">
145-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightGoldenrodYellow" />
146-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightYellow" />
147-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightGoldenrodYellow" />
148-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightYellow" />
149-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightGoldenrodYellow" />
150-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightYellow" />
151-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightGoldenrodYellow" />
152-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightYellow" />
153-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightGoldenrodYellow" />
154-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightYellow" />
155-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightGoldenrodYellow" />
156-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightYellow" />
157-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightGoldenrodYellow" />
158-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightYellow" />
159-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightGoldenrodYellow" />
160-
<BoxView HeightRequest="60" WidthRequest="80" Color="LightYellow" />
161-
</dui:VerticalStackLayout>
162-
</dui:HorizontalStackLayout>
163-
</dui:ScrollView>
19+
<dui:Button Clicked="Button_OnClicked" />
16420

16521
</Grid>
16622

src/app/Playground/VetleSamples/VetlePage.xaml.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@
88
using DIPS.Mobile.UI.Extensions;
99
using DIPS.Mobile.UI.Resources.Icons;
1010
using Microsoft.Maui.Controls.PlatformConfiguration;
11+
using Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific;
1112
using Playground.HåvardSamples;
1213
using CollectionView = DIPS.Mobile.UI.Components.Lists.CollectionView;
1314
using Colors = DIPS.Mobile.UI.Resources.Colors.Colors;
15+
using NavigationPage = Microsoft.Maui.Controls.NavigationPage;
1416
using Shell = DIPS.Mobile.UI.Components.Shell.Shell;
1517

1618
namespace Playground.VetleSamples;
@@ -239,7 +241,12 @@ private void ListItem_OnTapped(object sender, EventArgs e)
239241

240242
private void Button_OnClicked(object sender, EventArgs e)
241243
{
242-
/*ScrollView.InvalidateMeasure();*/
244+
var page = new VetleTestPage1();
245+
var navigationPage = new NavigationPage(page);
246+
navigationPage.On<Microsoft.Maui.Controls.PlatformConfiguration.iOS>()
247+
.SetModalPresentationStyle(UIModalPresentationStyle.PageSheet);
248+
249+
Navigation.PushModalAsync(navigationPage);
243250
}
244251

245252
private void Button_OnClicked2(object sender, EventArgs e)
@@ -251,4 +258,5 @@ private void Button_OnClicked3(object sender, EventArgs e)
251258
{
252259
_ = Navigation.PushAsync(new VetleTestPage1());
253260
}
261+
254262
}

src/library/DIPS.Mobile.UI/API/Builder/DIPSUIOptions.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,5 +53,4 @@ public IDIPSUIOptions AddStartDictationDelegate(Func<IDictationConsumerDelegate,
5353

5454
return this;
5555
}
56-
5756
}

0 commit comments

Comments
 (0)