MemoryToolkit.Maui
MemoryToolkit.Maui copied to clipboard
System.ObjectDisposedException happens randomly
Is there any chance we catch this in TearDown() ? Since sometimes Handlers may be already disposed explicitly or by framework. check #25
For us it isn't random, but easily reproduced:
- Open a page that has the
TearDownBehavior - Background the app
- Resume the app
- Navigate back
- Observe crash
@reid-kirkpatrick Are you getting any error log? . I mean were you able to identify which element's handler is already disposed?
@reid-kirkpatrick Are you getting any error log? . I mean were you able to identify which element's handler is already disposed?
Unfortunately the stacktrace doesn't include that
@reid-kirkpatrick It's better to not use plugin in your case. You can teardown each page before popping out of it by writing your own teardown method
public async Task NavigateBackContentPageAsync()
{
if (Navigation.NavigationStack.Count > 0)
{
var page = Navigation.NavigationStack.Last();
page.TearDown();
await Navigation.PopAsync();
}
}
and custom teardown goes like this
public static void TearDown(this IVisualTreeElement vte)
{
TearDownImpl(vte, true);
return;
void TearDownImpl(IVisualTreeElement vte, bool isRoot)
{
if (vte is not BindableObject bindableObject)
return;
foreach (IVisualTreeElement childElement in vte.GetVisualChildren())
TearDownImpl(childElement, false);
if (vte is VisualElement visualElement)
{
// First, clear the BindingContext
visualElement.BindingContext = null;
// Next, isolate the element.
visualElement.Parent = null;
if (vte is ListView listView)
listView.ItemsSource = null;
else if (vte is ContentView contentView)
contentView.Content = null;
else if (vte is Border border)
border.Content = null;
else if (vte is ContentPage contentPage)
contentPage.Content = null;
else if (vte is ScrollView scrollView)
scrollView.Content = null;
visualElement.ClearLogicalChildren();
// With the binding context cleared, and the element isolated, it has a chance to revert itself
// to a 'default' state.
// The _last_ thing we want to do is disconnect the handler.
if (visualElement.Handler != null)
{
if (visualElement.Handler is IDisposable disposableHandler)
{
try
{
disposableHandler.Dispose();
}
catch(ObjectDisposedException ex)
{
}
}
visualElement.Handler?.DisconnectHandler();
}
visualElement.Resources = null;
}
else if (vte is Element element)
{
element.BindingContext = null;
element.Parent = null;
element.ClearLogicalChildren();
if (element.Handler != null)
{
#if IOS
// Fixes issue specific to ListView on iOS, where RealCell is not nulled out.
if (element is ViewCell && element.Handler.PlatformView is IDisposable disposablePlatformView)
disposablePlatformView.Dispose();
#endif
if (element.Handler is IDisposable disposableElementHandler)
{
try
{
disposableElementHandler.Dispose();
}
catch (ObjectDisposedException ex)
{
}
}
element.Handler.DisconnectHandler();
}
}
}
}
In this we can catch the ObjectDisposedException