MemoryToolkit.Maui icon indicating copy to clipboard operation
MemoryToolkit.Maui copied to clipboard

System.ObjectDisposedException happens randomly

Open bhavanesh2001 opened this issue 1 year ago • 4 comments

Is there any chance we catch this in TearDown() ? Since sometimes Handlers may be already disposed explicitly or by framework. check #25

bhavanesh2001 avatar Oct 10 '24 06:10 bhavanesh2001

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 avatar Oct 24 '24 19:10 reid-kirkpatrick

@reid-kirkpatrick Are you getting any error log? . I mean were you able to identify which element's handler is already disposed?

bhavanesh2001 avatar Oct 25 '24 02:10 bhavanesh2001

@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 avatar Oct 28 '24 12:10 reid-kirkpatrick

@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

bhavanesh2001 avatar Oct 28 '24 12:10 bhavanesh2001