如何修复由lambda事件处理程序引起的GC循环?

Dan*_*mov 5 c# mono garbage-collection xamarin.ios xamarin

通过Mark Probst和Rodrigo Kumpera 观察高级内存管理,我学习了新的技术,例如分析Mono GC和使用WeakReference.

然而,我仍然不明白如何从第28分钟"修复"拼图2:

public class CustomButton : UIButton {
    public CustomButton () { }
}

public class Puzzle2Controller : UIViewController
{
    public override void ViewDidLoad ()
    {
        var button = new CustomButton ();
        View.Add (button);
        button.TouchUpInside += (sender, e) =>
            this.RemoveFromParentViewController ();
    }
}
Run Code Online (Sandbox Code Playgroud)

控制器保存一个ref to按钮,该按钮保存一个ref事件处理程序,该处理程序将ref保存到控制器.

打破循环的一种方法是使按钮无效.另一种方法是分离处理程序(但我们必须抛弃使用lamdas).

是否有其他/更优雅/方式来打破这个循环?我们能以某种方式坚持WeakReference到这里吗?

谢谢.

编辑:在这种情况下,按钮甚至不是一个字段.但是还有一个周期,不是吗?它位于Controller的View的子视图中.我们必须清除它们吗?我糊涂了.

Mar*_*ell 6

在垃圾收集环境中,循环通常不是问题 - 我必须从问题中假设这在单调处有些不同?编辑:不,我在这里的假设仍然有效 - 在你连接到它的视频中的7:50表示"扫描"丢弃了一个循环.

一旦整个周期变得无法到达,周期通常可以完全销售.这将是参考计数系统中的问题.

然而!关于你的问题 - 按钮(一旦添加)是否知道控制器?如果是这样,你可以从那里到达sender:

button.TouchUpInside += (sender, e) =>
            ((UIButton)sender).Parent.RemoveFromParentViewController();
Run Code Online (Sandbox Code Playgroud)

此lambda现在不涉及捕获的变量,并且不涉及捕获上下文; 它没有任何对控制器的引用 - 实际上大多数编译器会将它作为单个静态处理程序而不是每次使用的处理程序,因此它在委托创建方面也更有效.


特别是单调的背景,然后是的,你需要使用WeakReference<T>:

var controller = new WeakReference<Puzzle2Controller>(this);
button.TouchUpInside += (sender, e) => {
    var parent = controller.Object;
    if(parent != null) parent.RemoveFromParentViewController();
};
Run Code Online (Sandbox Code Playgroud)