From 247f758523c8bce9162b0a7192b24b53a9d4ae86 Mon Sep 17 00:00:00 2001 From: kicsiede <4233752+kicsiede@users.noreply.github.com> Date: Wed, 8 May 2019 19:41:08 +0200 Subject: [PATCH] [iOS] Fix: memory leak when Navigation.RemovePage is used (#5695) * Fix: memoryleak when Navigation.RemovePage is used The leak occurs in a MasterDetailPage.Detail = NavigationPage(ContentPage) scenario if the ContentPage has ToolBarItems. (ToolBarItems create a strong dependency therefore the ViewController of the NavigationPage must be explicitly disposed) * + unit test * fix issue attribute --- .../Issue5695.cs | 96 ++++++++++++++++++++++ .../Xamarin.Forms.Controls.Issues.Shared.projitems | 1 + .../Renderers/NavigationRenderer.cs | 1 + 3 files changed, 98 insertions(+) create mode 100644 Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue5695.cs diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue5695.cs b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue5695.cs new file mode 100644 index 0000000..ae13a19 --- /dev/null +++ b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue5695.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using Xamarin.Forms.CustomAttributes; +using Xamarin.Forms.Internals; + +#if UITEST +using NUnit.Framework; +using Xamarin.UITest; +using Xamarin.Forms.Core.UITests; +#endif + +namespace Xamarin.Forms.Controls.Issues +{ + [Preserve(AllMembers = true)] + [Issue(IssueTracker.Github, 5695, "Memory leak when Navigation.RemovePage", PlatformAffected.iOS)] + public class Issue5695 : TestMasterDetailPage + { + [Preserve(AllMembers = true)] + class LeakPage : ContentPage + { + public LeakPage() + { + ToolbarItems.Add(new ToolbarItem { Text = "Dummy" }); + } + } + + protected override void Init () + { + var wref = new WeakReference(null); + + var result = new Label { + FontSize = 16, + Text = "Click 'Push page'" + }; + + var checkResult = new Button + { + Text = "Check Result", + IsEnabled = false, + Command = new Command(() => + { + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); + + result.Text = wref.IsAlive ? "Failed" : "Success"; + }) + }; + + Detail = new NavigationPage(new LeakPage()); + + Master = new ContentPage + { + Title = "menu", + Content = new StackLayout + { + Children = { + result, + new Button + { + Text = "Push page", + Command = new Command(async() => { + + await Detail.Navigation.PushAsync(new LeakPage()); + + var pageToRemove = Detail.Navigation.NavigationStack[0]; + + Detail.Navigation.RemovePage(pageToRemove); + + wref.Target = pageToRemove; + + checkResult.IsEnabled = true; + result.Text = "You can check result"; + }) + }, + checkResult + } + } + }; + } + +#if UITEST + [Test] + public void Issue5695Test() + { + RunningApp.Tap(q => q.Marked("Push page")); + RunningApp.WaitForElement(q => q.Marked("Push page")); + + RunningApp.WaitForElement(q => q.Marked("You can check result")); + RunningApp.Tap(q => q.Marked("Check Result")); + + RunningApp.WaitForElement(q => q.Marked("Success")); + } +#endif + } +} \ No newline at end of file diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems index c134900..1ed6663 100644 --- a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems +++ b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems @@ -465,6 +465,7 @@ Issue5003.xaml Code + diff --git a/Xamarin.Forms.Platform.iOS/Renderers/NavigationRenderer.cs b/Xamarin.Forms.Platform.iOS/Renderers/NavigationRenderer.cs index 3436edc..c38f4fc 100644 --- a/Xamarin.Forms.Platform.iOS/Renderers/NavigationRenderer.cs +++ b/Xamarin.Forms.Platform.iOS/Renderers/NavigationRenderer.cs @@ -587,6 +587,7 @@ namespace Xamarin.Forms.Platform.iOS _removeControllers = _removeControllers.Remove(target); ViewControllers = _removeControllers; } + target.Dispose(); var parentingViewController = ViewControllers.Last() as ParentingViewController; parentingViewController?.UpdateLeftBarButtonItem(page); } -- 2.7.4