2

我有一个具有复杂视图层次结构的应用程序,其中一个视图控制器(我们称之为 RootVC)可以导致许多其他视图控制器。当我弹出 RootVC 时,我想完全释放它。我没有将引用存储在字段或其他地方,并且相信它就足够了。但最近我检查了 Xamarin 的 Heap Shot 并在“所有对象”部分看到许多 RootVC 实例仍保留在内存中。

例如这个:

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):
    }
}

这是 Heap Shot 截图的链接(由于声誉低,不能将它们作为图片发布)- http://imgur.com/a/3MYBr#1

当我在 VC2 上时,我看到类似于第一个屏幕截图的内容。

当我弹出 VC2 和 VC1 时,Heap Shot 看起来像第二张图像。

在出现几个 VC2 之后,它看起来像第三个屏幕截图。

而且它们永远不会被处理或最终确定,它们会一直保留到最后。即使通过它们也无法通过根访问。这是不可接受的,因为内存是有限的,并且所有这些 VC2 都在内存中,我们很容易收到内存警告或应用程序终止。

但是,如果我订阅/取消订阅 View 上出现/消失的按钮事件,如下所示:

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):
    }
}

在这种情况下,VC2(及其所有视图等)在消失时会被正确处理。~VC2() 被调用,所有的 VC2 不再在 Heap Shot 中可见。

所以,这就是问题所在。我做错了什么?我应该订阅/取消订阅上述控制事件吗?还是 Xamarin.iOS 的内存泄漏?

4

1 回答 1

4

发生的情况是存在跨越本机托管边界的循环依赖项(使用事件处理程序),并且当前 Xamarin.iOS 运行时/GC 无法检测到这一点。

您已经找到了解决此问题的方法:通过删除事件处理程序来打破循环依赖。

这是一个详细解释情况的视频:http: //xamarin.com/evolve/2013#session-0w86u7bco2

于 2013-10-11T07:22:20.523 回答