0

What's the proper way to dispose of a CAKeyFrameAnimation that has custom events? I made a test project demonstrating exactly what I'm doing in the simplest way possible.

Here is my Xamarin Studio Project

Here is my XCode Instruments Memory Allocations Trace

To reproduce what I did during this memory allocations trace, simply run the project. Press the green button 10 times. (Every time you press the green button, the blue square animates).
Then evaluate the memory usage. Notice in my CreateAnimationEventHandler_Position_AnimationStopped function, I'm disposing of the animation. But also notice that Instruments is telling me I have a TON of non-objects that are leaking memory... What's causing that? And how do I dispose of CAKeyFrameAnimations correctly?

I tried a few different ways to dispose of it. Here are my results. After just 10 button presses I get around 30-100kb of non-objects absorbing memory. I must be doing something wrong.

Without Disposal

With Disposal on animation only not path nor events, more details

With Disposal, more details

And here's some code to look at (but it's also in the project attached):

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

        // Perform any additional setup after loading the view, typically from a nib.



        UIImageView ViewToAnimation = new UIImageView(new RectangleF(100, 100, 100, 100));
        ViewToAnimation.BackgroundColor = UIColor.Blue;
        View.AddSubview(ViewToAnimation);



        GoButton = new UIButton(new RectangleF(0, 0, 100, 100));
        GoButton.BackgroundColor = UIColor.Green;
        View.AddSubview(GoButton);
        GoButton.TouchUpInside += delegate {            
            animation = CreateAnimation_Position(ViewToAnimation, new Random().Next(0,500), new Random().Next(0,700), ViewToAnimation.Center.X, ViewToAnimation.Center.Y, 0.4f, CAMediaTimingFunction.FromName (CAMediaTimingFunction.Linear));
            ViewToAnimation.Layer.AddAnimation(animation, "animation");
        };


    }


    public CAKeyFrameAnimation CreateAnimation_Position ( UIView view, float toX, float toY, float fromX, float fromY, float duration_s, CAMediaTimingFunction timingFunction )
    {
        AppDelegate appDel = (AppDelegate)UIApplication.SharedApplication.Delegate;

        CAKeyFrameAnimation positionAnimation = CAKeyFrameAnimation.GetFromKeyPath ("position");
        positionAnimation.Duration = duration_s;
        positionAnimation.TimingFunction = timingFunction;

        PointF toLocation = new PointF(toX, toY);

        // Make a path for this animation
        CGPath path = new CGPath();
        PointF[] lines = {new PointF(fromX, fromY), new PointF(toX, toY)};
        path.AddLines(lines);

        positionAnimation.Path = path;

        event_started = CreateAnimationEventHandler_Position_AnimationStarted(view, toLocation);
        positionAnimation.AnimationStarted += event_started;

        event_stopped = CreateAnimationEventHandler_Position_AnimationStopped(view, positionAnimation);
        positionAnimation.AnimationStopped += event_stopped;

        return positionAnimation;
    }


    public EventHandler CreateAnimationEventHandler_Position_AnimationStarted ( UIView view, PointF toLocation )
    {
        EventHandler animationStartedEvent = delegate {
            // Apply the Position change to the Layer
            view.Layer.Position = toLocation;
        };

        return animationStartedEvent;
    }


    public EventHandler<CAAnimationStateEventArgs> CreateAnimationEventHandler_Position_AnimationStopped ( UIView view, CAKeyFrameAnimation animationToDispose )
    {
        EventHandler<CAAnimationStateEventArgs> animationStoppedEvent = delegate(object sender, CAAnimationStateEventArgs e) {
//              Console.WriteLine("Finished!  finished = " + e.Finished);


//              Console.WriteLine("Removing and Disposing Events");
//              animationToDispose.AnimationStarted -= event_started;
//              animationToDispose.AnimationStopped -= event_stopped;
//              event_started = null;
//              event_stopped = null;
//
//
//              Console.WriteLine("Disposing Animation");
//              animationToDispose.Path.Dispose();


            // For some reason, only calling Dispose on the animation and NOT calling dispose on the Path & events seems to be more efficient... no idea why
            animationToDispose.Dispose();
        };

        return animationStoppedEvent;
    }
LampShade
  • 2,675
  • 5
  • 30
  • 60

1 Answers1

2

The great people at Xamarin Support gave me some good resources on good memory practices and showed me that non-objects are not always memory leaks. In this case, they are not. So it turns out that I'm disposing correctly.

Thanks to all who read this and considered helping.

http://docs.xamarin.com/guides/cross-platform/application_fundamentals/memory_perf_best_practices/

LampShade
  • 2,675
  • 5
  • 30
  • 60