xamarin.ios - ¿Es esta una pérdida de memoria en Xamarin Forms?
xamarin.forms (1)
Creo que lo que está viendo es un efecto secundario de la navegación asíncrona, no la pérdida de memoria. En lugar de WeakReferences, puede optar por un finalizador y crear instancias de MyPage (en lugar de ContentPage).
public class MyPage: ContentPage
{
private static int count;
public MyPage()
{
count++;
Debug.WriteLine("Created total " + count);
}
~MyPage()
{
count--;
Debug.WriteLine("Finalizer, remaining " + count);
}
}
El siguiente truco es agregar una llamada GC.Collect () retrasada, como:
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();
}
Notará que las instancias obtienen basura recolectada dentro de esta recolección demorada (ventana de salida). Según el recolector de basura Xamarin: Dudo que tenga fallas serias. Un error menor aquí y allá, pero no tan grande. Dicho esto, lidiar con las recolecciones de basura en Android es particularmente difícil porque hay dos de ellas, Dalvik y Xamarin. Pero esa es otra historia.
Me he encontrado con un problema en el que parece que los objetos de la página no se recogen en la basura una vez que se han navegado. He reunido un ejemplo muy básico de esto que demuestra el problema al usar una página de navegación y el método PushAsync. La página muestra el número de páginas ''Alive'' usando una lista de referencias débiles:
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;
}
}
Al hacer clic en el botón Página siguiente, se crea una nueva página con una etiqueta de valor fijo que muestra el número de referencias de páginas activas en el momento en que se creó esta página. Cada vez que haga clic en el botón, obviamente verá que este número aumenta en 1. Entiendo que cuando hace clic en ''atrás'' en la página de navegación, la vista se debe quitar de la pila y desechar (permitiendo que se coloque en GC) . Sin embargo, cuando ejecuto este código de prueba, indica que después de volver, esta vista se conserva en la memoria. Esto se puede demostrar haciendo clic en Siguiente página varias veces hasta que el recuento de referencia esté en 3. Si luego hace clic en Atrás y luego en Página siguiente, creo que el recuento de referencia aún debería ser 3 (lo que indica que la página anterior tenía GC antes de la nueva uno fue creado) sin embargo, el nuevo recuento de referencia es ahora 4.
Esto parece ser un error bastante serio en la implementación de navegación de X-Forms para iOS (no lo he probado para otras plataformas), supongo que está relacionado de alguna manera con el problema del ciclo de referencia fuerte descrito aquí: http://developer.xamarin.com/guides/cross-platform/application_fundamentals/memory_perf_best_practices/
¿Alguien más ha encontrado esto y / o ha encontrado una solución / solución para esto? ¿Alguien más estaría de acuerdo en que esto es un error?
Como complemento, hice un segundo ejemplo que no involucra una página de navegación (por lo que tiene que usar PushModalAsync en su lugar) y descubrí que tenía el mismo problema, por lo que este problema no parece ser exclusivo de la navegación de la página de navegación. Para referencia, el código para esa prueba (muy similar) está aquí:
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;
}
}