简体   繁体   中英

Monotouch (Xamarin.iOS) memory leaks

I have an app with a complicated view hierarchy, where one view controller (let's call it RootVC) can lead to many others. When I pop RootVC, I want to completely release it. I didn't store the reference in a field or elsewhere and believed that it would be enough. But recently I checked Xamarin's Heap Shot and saw in the "All objects" section that many instances of RootVC remain in memory.

For example this:

sealed class VC1 : UIViewController
{
    ...

    private void OpenVC2()
    {
        var vc2 = new VC2();
        NavigationController.PushViewController(vc2, true);
    }
}

sealed class VC2 : UIViewController
{
    private UIButton button;
    public override ViewDidLoad()
    {
        ...
        button = new UIButton(frame);
        button.TouchUpInside += HandleButtonTouch;
        ...
    }

    private void HandleButtonTouch(object sender, EventArgs e)
    {
        NavigationController.PopViewControllerAnimated(true):
    }
}

Here's the link to the screenshots of Heap Shot (can't post them as images because of low reputation) - http://imgur.com/a/3MYBr#1

When I'm on VC2, I see something like on the first screenshot.

When I pop VC2 and on VC1, Heap Shot look like on the second image.

After a few VC2 appearing it looks like the third screenshot.

And they never get disposed or finalized, they remain till the end. Even through they are not reachable via roots. Which is not acceptable because memory is limited and with all these VC2's in memory we can easily get memory warning or app terminating.

But if I subscribe/unsubscribe to button events on View appearing/disappearing like this:

sealed class VC2 : UIViewController
{
    private UIButton button;
    public override ViewDidLoad()
    {
        ...
        button = new UIButton(frame);
        ...
    }

    public override void ViewWillAppear(bool animated)
    {
        ...
        button.TouchUpInside += HandleButtonTouch;
    }

    public override void ViewDidDisappear(bool animated)
    {
        ...
        button.TouchUpInside -= HandleButtonTouch;
    }

    private void HandleButtonTouch(object sender, EventArgs e)
    {
        NavigationController.PopViewControllerAnimated(true):
    }
}

In this case VC2 (with all its views, etc) gets disposed properly when it disappears. ~VC2() is called and all VC2's are no longer visible in Heap Shot.

So, here's the question. What I am doing wrong? Should I subscribe/unsubscribe from control events like above? Or it's a Xamarin.iOS's memory leak?

What happens is that there is a circular dependency (with the event handlers) that crosses the native-managed boundary, and currently the Xamarin.iOS runtime/GC is not able to detect this.

You've already found the fix for this problem: break the circular dependency by removing event handlers.

Here is a video explaining the situation in detail: http://xamarin.com/evolve/2013#session-0w86u7bco2

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM