CALayerDelegate disposing issue

2019-08-05 08:57发布

问题:

The issue actually is valid for Xamarin.iOS. However, I decided to add iOS tag to this question as well, just in case.

I have managed to reproduce the issue with extremely simple example. The app consists of UINavigationController and has 2 pages. First page has just the button to navigate to second page. Second page adds new UIView to main view of UIViewController. It also assigns CALayerDelegate to the Layer property of this new UIView. The issue occurs when second UIViewController is garbage collected (you should go back and forth to the second page multiple times till it may happen). When it happens, the app crashes without any issue thrown to the debugger. Some investigation showed that this happens because the runtime tries to deallocate already release object (as far as I understand it). I can trace following error when using Instruments:

An Objective-C message was sent to a deallocated 'Test.TestLayerDelegate' object (zombie) at address: 0x14d597c0.

My question is whether this is the bug in Xamarin or in the code itself. And how should I fix it.

Here is the code for UIViewController (the issue occurs in both cases)

using System;
using System.Drawing;
using MonoTouch.Foundation;
using MonoTouch.UIKit;
using MonoTouch.CoreAnimation;

namespace Test
{
    public partial class TestViewController : UIViewController
    {
        private UIView _testView;

        public TestViewController (IntPtr handle) : base (handle)
        {
        }

        public override void ViewDidLoad ()
        {
            base.ViewDidLoad ();

            _testView = new UIView (View.Frame);
            View.Add (_testView);
            //case 1
            _testView.Layer.Delegate = new TestLayerDelegate ();
            //case 2
            //_testView.Layer.Delegate = new CALayerDelegate ();
            _testView.Layer.SetNeedsDisplay ();
        }

        protected override void Dispose (bool disposing)
        {
            Console.WriteLine ("---dispose {0}", this);
            base.Dispose (disposing);
            Console.WriteLine ("---postdispose {0}", this);
        }
    }
}

Here is the code for TestLayerDelegate

using MonoTouch.CoreAnimation;
using MonoTouch.CoreGraphics;
using System;
using System.Drawing;
using MonoTouch.UIKit;

namespace Test
{
    public class TestLayerDelegate : CALayerDelegate
    {
        public override void DrawLayer(CALayer layer, CGContext context)
        {
            context.SetFillColor(UIColor.Green.CGColor);
            context.FillRect(layer.Frame);
        }

        protected override void Dispose (bool disposing)
        {
            Console.WriteLine ("---dispose {0}", this);
            base.Dispose (disposing);
            Console.WriteLine ("---postdispose {0}", this);
        }      
    }
}