我遇到了一个问题,一旦它们被导航,它们就会出现Page对象没有被垃圾收集。我已经汇总了一个非常基本的示例,它演示了使用NavigationPage和PushAsync方法时的问题。该页面显示' Alive'使用弱引用列表的页面:
public class AppNavigationPage
{
private static List<WeakReference> pageRefs = new List<WeakReference>();
public static Page GetMainPage()
{
return new NavigationPage(CreateWeakReferencedPage());
}
private static Page CreateWeakReferencedPage()
{
GC.Collect();
var result = CreatePage();
pageRefs.Add(new WeakReference(result));
// Add a second unreferenced page to prove that the problem only exists
// when pages are actually navigated to/from
pageRefs.Add(new WeakReference(CreatePage()));
GC.Collect();
return result;
}
private static Page CreatePage()
{
var page = new ContentPage();
var contents = new StackLayout();
contents.Children.Add(
new Button
{
Text = "Next Page",
Command = new Command(() => page.Navigation.PushAsync(CreateWeakReferencedPage()))
});
contents.Children.Add(
new Label
{
Text = string.Format(
"References alive at time of creation: {0}",
pageRefs.Count(p => p.IsAlive)),
HorizontalOptions = LayoutOptions.CenterAndExpand
});
page.Content = contents;
return page;
}
}
当您单击“下一页”按钮时,将创建一个带有固定值标签的新页面,该标签显示在创建此页面时活动的页面引用数。每次单击按钮,您都会看到此数字增加1.我的理解是,当您点击“返回”时在导航页面上,视图应从堆栈中弹出并丢弃(允许它为GC&#39; d)。但是,当我运行此测试代码时,它表示在我们返回之后,此视图将保留在内存中。这可以通过单击“下一页”几次直到引用计数为3来证明。如果然后单击“上一步”然后单击“下一页”,我相信引用计数仍应为3(表示旧页面是GC&#39; d之前新的参考计数现在已经创建了4。
对于iOS的X-Forms导航实现来说,这似乎是一个非常严重的错误(我还没有对其他平台进行测试),我的猜测是它在某种程度上与此处描述的强参考周期问题相关:{ {3}}
是否有其他人遇到此问题和/或为其提出解决方案/解决方法?有人会同意这是一个错误吗?
作为补充,我做了第二个没有涉及NavigationPage的例子(因此必须使用PushModalAsync)并发现我遇到了同样的问题,所以这个问题看起来并不是唯一的NavigationPage导航。作为参考,该(非常相似)测试的代码在这里:
public class AppModal
{
private static List<WeakReference> pageRefs = new List<WeakReference>();
public static Page GetMainPage()
{
return CreateWeakReferencedPage();
}
private static Page CreateWeakReferencedPage()
{
GC.Collect();
var result = CreatePage();
pageRefs.Add(new WeakReference(result));
// Add a second unreferenced page to prove that the problem only exists
// when pages are actually navigated to/from
pageRefs.Add(new WeakReference(CreatePage()));
GC.Collect();
return result;
}
private static Page CreatePage()
{
var page = new ContentPage();
var contents = new StackLayout();
contents.Children.Add(
new Button
{
Text = "Next Page",
Command = new Command(() => page.Navigation.PushModalAsync(CreateWeakReferencedPage()))
});
contents.Children.Add(
new Button
{
Text = "Close",
Command = new Command(() => page.Navigation.PopModalAsync())
});
contents.Children.Add(
new Label
{
Text = string.Format(
"References alive at time of creation: {0}",
pageRefs.Count(p => p.IsAlive)),
HorizontalOptions = LayoutOptions.CenterAndExpand
});
page.Content = contents;
return page;
}
}
答案 0 :(得分:2)
我认为你所看到的是异步导航的副作用,而不是内存泄漏。您可以选择使用终结器而不是WeakReferences来创建MyPage实例(而不是ContentPage)。
public class MyPage: ContentPage
{
private static int count;
public MyPage()
{
count++;
Debug.WriteLine("Created total " + count);
}
~MyPage()
{
count--;
Debug.WriteLine("Finalizer, remaining " + count);
}
}
下一个技巧是添加延迟的GC.Collect()调用,如:
private static Page CreateWeakReferencedPage()
{
GC.Collect();
var result = CreatePage();
var ignore = DelayedGCAsync();
return result;
}
private static async Task DelayedGCAsync()
{
await Task.Delay(2000);
GC.Collect();
}
您将注意到实例会在此延迟集合(输出窗口)中收集垃圾。 根据Xamarin GarbageCollector:我怀疑它有严重的缺陷。这里和那里的一个小错误但不是那么大。也就是说,处理Android中的垃圾收集特别棘手,因为其中有两个--Dalvik和Xamarin。但这是另一个故事。