Skip to content

Commit

Permalink
Don't return the same view for TableView if not enabling reuse
Browse files Browse the repository at this point in the history
  • Loading branch information
PureWeen committed Feb 2, 2024
1 parent 8c04a9d commit 1204cb0
Show file tree
Hide file tree
Showing 5 changed files with 211 additions and 0 deletions.
Expand Up @@ -102,6 +102,9 @@ protected override void Init()
#if UITEST
[Test]
[Compatibility.UITests.FailsOnMauiIOS]
#if ANDROID
[Compatibility.UITests.MovedToAppium]
#endif
public void Issue5555Test()
{
RunningApp.Tap(q => q.Marked("Push page"));
Expand Down
110 changes: 110 additions & 0 deletions src/Controls/samples/Controls.Sample.UITests/Issues/Issue5555.cs
@@ -0,0 +1,110 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Maui.Controls;

namespace Maui.Controls.Sample.Issues
{
[Issue(IssueTracker.None, 5555, "Memory leak when SwitchCell or EntryCell", PlatformAffected.iOS)]
public class Issue5555 : TestContentPage
{
public static Label DestructorCount = new Label() { Text = "0" };
protected override void Init()
{
var result = new Label
{
FontSize = 16,
Text = "Click 'Push page' twice"
};

var list = new List<WeakReference>();

var checkButton = new Button
{
Text = "Check Result",
IsEnabled = false,
Command = new Command(async () =>
{
if (list.Count < 2)
{
result.Text = "Click 'Push page' again";
return;
}
try
{
await GarbageCollectionHelper.WaitForGC(2500, list.ToArray());
result.Text = "Success";
}
catch (Exception)
{
result.Text = "Failed";
return;
}
})
};

Content = new StackLayout
{
Children = {
DestructorCount,
result,
new Button
{
Text = "Push page",
Command = new Command(async() => {
if (list.Count >= 2)
list.Clear();
var wref = new WeakReference(new LeakPage());
await Navigation.PushAsync(wref.Target as Page);
await (wref.Target as Page).Navigation.PopAsync();
list.Add(wref);
if (list.Count > 1)
{
checkButton.IsEnabled = true;
result.Text = "You can check result";
}
else
{
result.Text = "Again";
}
})
},
checkButton
}
};
}

class LeakPage : ContentPage
{
public LeakPage()
{
Content = new StackLayout
{
Children = {
new Entry { Text = "LeakPage" },
new TableView
{
Root = new TableRoot
{
new TableSection
{
new SwitchCell { Text = "switch cell", On = true },
new EntryCell { Text = "entry cell" }
}
}
}
}
};
}

~LeakPage()
{
System.Diagnostics.Debug.WriteLine("LeakPage Finalized");
}
}
}
}
@@ -0,0 +1,63 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;

namespace Maui.Controls.Sample
{
public static class GarbageCollectionHelper
{
public static void Collect()
{
GC.Collect();
GC.WaitForPendingFinalizers();

GC.Collect();
GC.WaitForPendingFinalizers();
}

public static async Task WaitForGC(params WeakReference[] references) => await WaitForGC(5000, references);

public static async Task WaitForGC(int timeout, params WeakReference[] references)
{
bool referencesCollected()
{
GC.Collect();
GC.WaitForPendingFinalizers();

foreach (var reference in references)
{
if (reference.IsAlive)
{
return false;
}
}

return true;
}

await AssertEventually(referencesCollected, timeout);
}

public static async Task AssertEventually(this Func<bool> assertion, int timeout = 1000, int interval = 100, string message = "Assertion timed out")
{
do
{
if (assertion())
{
return;
}

await Task.Delay(interval);
timeout -= interval;

}
while (timeout >= 0);

if (!assertion())
{
throw new Exception(message);
}
}
}
}
Expand Up @@ -141,6 +141,13 @@ public override AView GetView(int position, AView convertView, ViewGroup parent)

AView nativeCellContent = CellFactory.GetCell(item, convertView, parent, Context, _view);

// If we're not using recycling then we can't return the same instance of the view with each call to GetView
// Android expects this to always return a new view
if (convertView is null && nativeCellContent.Parent is ViewGroup vg)
{
vg.RemoveAllViews();
}

// The cell content we get back might already be in a ConditionalFocusLayout; if it is,
// we'll just use that. If not, we'll need to create one and add the content to it

Expand Down
28 changes: 28 additions & 0 deletions src/Controls/tests/UITests/Tests/Issues/Issue5555.cs
@@ -0,0 +1,28 @@
using NUnit.Framework;
using UITest.Appium;
using UITest.Core;

namespace Microsoft.Maui.AppiumTests.Issues
{
public class Issue5555 : _IssuesUITest
{
public override string Issue => "Memory leak when SwitchCell or EntryCell";
public Issue5555(TestDevice device) : base(device)
{
}

[Test]
public void Issue5555Test()
{
App.Click("Push page");
App.WaitForElement("Push page");
App.Click("Push page");
App.WaitForElement("Push page");

App.WaitForElement("You can check result");
App.Click("Check Result");

App.WaitForElement("Success");
}
}
}

0 comments on commit 1204cb0

Please sign in to comment.