--- /dev/null
+using System;
+using System.Diagnostics;
+using System.Threading;
+using System.Threading.Tasks;
+using Xamarin.Forms.CustomAttributes;
+using Xamarin.Forms.Internals;
+
+#if UITEST
+using Xamarin.UITest;
+using NUnit.Framework;
+#endif
+
+namespace Xamarin.Forms.Controls
+{
+ [Preserve(AllMembers = true)]
+ [Issue(IssueTracker.Bugzilla, 44166, "MasterDetailPage instances do not get disposed upon GC")]
+ public class Bugzilla44166 : TestContentPage
+ {
+ protected override void Init()
+ {
+ var label = new Label() { Text = "Testing..." };
+
+ var goButton = new Button { Text = "Go" };
+ goButton.Clicked += (sender, args) => Application.Current.MainPage = new _44166MDP();
+
+ var gcButton = new Button { Text = "GC" };
+ gcButton.Clicked += (sender, args) =>
+ {
+ GC.Collect();
+ GC.WaitForPendingFinalizers();
+
+ if (_44166MDP.Counter > 0)
+ {
+ Debug.WriteLine($">>>>>>>> Post-GC, {_44166MDP.Counter} {nameof(_44166MDP)} allocated");
+ }
+
+ if (_44166Master.Counter > 0)
+ {
+ Debug.WriteLine($">>>>>>>> Post-GC, {_44166Master.Counter} {nameof(_44166Master)} allocated");
+ }
+
+ if (_44166Detail.Counter > 0)
+ {
+ Debug.WriteLine($">>>>>>>> Post-GC, {_44166Detail.Counter} {nameof(_44166Detail)} allocated");
+ }
+
+ if (_44166NavContent.Counter > 0)
+ {
+ Debug.WriteLine($">>>>>>>> Post-GC, {_44166NavContent.Counter} {nameof(_44166NavContent)} allocated");
+ }
+
+ if (_44166NavContent.Counter + _44166Detail.Counter + _44166Master.Counter + _44166MDP.Counter == 0)
+ {
+ label.Text = "Success";
+ }
+ };
+
+ Content = new StackLayout
+ {
+ Children = { label, goButton, gcButton }
+ };
+ }
+
+ #if UITEST
+ [Test]
+ public void Bugzilla44166Test()
+ {
+ RunningApp.WaitForElement(q => q.Marked("Go"));
+ RunningApp.Tap(q => q.Marked("Go"));
+
+ RunningApp.WaitForElement(q => q.Marked("Back"));
+ RunningApp.Tap(q => q.Marked("Back"));
+
+ for(int n = 0; n < 10; n++)
+ {
+ RunningApp.WaitForElement(q => q.Marked("GC"));
+ RunningApp.Tap(q => q.Marked("GC"));
+
+ if (RunningApp.Query(q => q.Marked("Success")).Length > 0)
+ {
+ return;
+ }
+ }
+
+ var pageStats = string.Empty;
+
+ if (_44166MDP.Counter > 0)
+ {
+ pageStats += $"{_44166MDP.Counter} {nameof(_44166MDP)} allocated; ";
+ }
+
+ if (_44166Master.Counter > 0)
+ {
+ pageStats += $"{_44166Master.Counter} {nameof(_44166Master)} allocated; ";
+ }
+
+ if (_44166Detail.Counter > 0)
+ {
+ pageStats += $"{_44166Detail.Counter} {nameof(_44166Detail)} allocated; ";
+ }
+
+ if (_44166NavContent.Counter > 0)
+ {
+ pageStats += $"{_44166NavContent.Counter} {nameof(_44166NavContent)} allocated; ";
+ }
+
+ Assert.Fail($"At least one of the pages was not collected: {pageStats}");
+ }
+ #endif
+ }
+
+ [Preserve(AllMembers = true)]
+ public class _44166MDP : MasterDetailPage
+ {
+ public static int Counter;
+
+ public _44166MDP()
+ {
+ Interlocked.Increment(ref Counter);
+ Debug.WriteLine($"++++++++ {nameof(_44166MDP)} constructor, {Counter} allocated");
+
+ Master = new _44166Master();
+ Detail = new _44166Detail();
+ }
+
+ ~_44166MDP()
+ {
+ Interlocked.Decrement(ref Counter);
+ Debug.WriteLine($"-------- {nameof(_44166MDP)} destructor, {Counter} allocated");
+ }
+ }
+
+ [Preserve(AllMembers = true)]
+ public class _44166Master : ContentPage
+ {
+ public static int Counter;
+
+ public _44166Master()
+ {
+ Interlocked.Increment(ref Counter);
+ Debug.WriteLine($"++++++++ {nameof(_44166Master)} constructor, {Counter} allocated");
+
+ Title = "Master";
+ var goButton = new Button { Text = "Back" };
+ goButton.Clicked += (sender, args) => Application.Current.MainPage = new Bugzilla44166();
+
+ Content = new StackLayout
+ {
+ Children = { goButton }
+ };
+ }
+
+ ~_44166Master()
+ {
+ Interlocked.Decrement(ref Counter);
+ Debug.WriteLine($"-------- {nameof(_44166Master)} destructor, {Counter} allocated");
+ }
+ }
+
+ [Preserve(AllMembers = true)]
+ public class _44166Detail : NavigationPage
+ {
+ public static int Counter;
+
+ public _44166Detail()
+ {
+ Interlocked.Increment(ref Counter);
+ Debug.WriteLine($"++++++++ {nameof(_44166Detail)} constructor, {Counter} allocated");
+
+ Title = "Detail";
+ PushAsync(new _44166NavContent());
+ }
+
+ ~_44166Detail()
+ {
+ Interlocked.Decrement(ref Counter);
+ Debug.WriteLine($"-------- {nameof(_44166Detail)} destructor, {Counter} allocated");
+ }
+ }
+
+ [Preserve(AllMembers = true)]
+ public class _44166NavContent : ContentPage
+ {
+ public static int Counter;
+
+ public _44166NavContent ()
+ {
+ Interlocked.Increment(ref Counter);
+ Debug.WriteLine($"++++++++ {nameof(_44166NavContent)} constructor, {Counter} allocated");
+
+ var goButton = new Button { Text = "Back" };
+ goButton.Clicked += (sender, args) => Application.Current.MainPage = new Bugzilla44166();
+
+ Content = new StackLayout
+ {
+ Children = { goButton }
+ };
+ }
+
+ ~_44166NavContent ()
+ {
+ Interlocked.Decrement(ref Counter);
+ Debug.WriteLine($"-------- {nameof(_44166NavContent)} destructor, {Counter} allocated");
+ }
+ }
+}
\ No newline at end of file
PageContainer _pageContainer;
FragmentManager _fragmentManager;
readonly bool _isMaster;
- readonly MasterDetailPage _parent;
+ MasterDetailPage _parent;
Fragment _currentFragment;
+ bool _disposed;
public MasterDetailContainer(MasterDetailPage parent, bool isMaster, Context context) : base(parent, isMaster, context)
{
}
}
+ protected override void Dispose(bool disposing)
+ {
+ if (_disposed)
+ {
+ return;
+ }
+
+ _disposed = true;
+
+ if (disposing)
+ {
+ if (_currentFragment != null)
+ {
+ FragmentTransaction transaction = FragmentManager.BeginTransaction();
+ transaction.DisallowAddToBackStack();
+ transaction.Remove(_currentFragment);
+ transaction.SetTransition((int)FragmentTransit.None);
+ transaction.CommitAllowingStateLoss();
+ FragmentManager.ExecutePendingTransactions();
+
+ _currentFragment = null;
+ }
+
+ _parent = null;
+ _pageContainer = null;
+ _fragmentManager = null;
+ }
+
+ base.Dispose(disposing);
+ }
+
public void SetFragmentManager(FragmentManager fragmentManager)
{
if (_fragmentManager == null)