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

Posted

技术标签:

【中文标题】如何修复由 lambda 事件处理程序引起的 GC 周期?【英文标题】:How do I fix the GC cycle caused by a lambda event handler? 【发布时间】:2013-06-10 23:09:02 【问题描述】:

观看 Mark Probst 和 Rodrigo Kumpera 的 Advanced Memory Management,我学到了新技术,例如分析 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 ();
    

控制器持有一个对按钮的引用,该按钮持有一个对事件处理程序的引用,该事件处理程序持有对控制器的一个引用。

打破循环的一种方法是取消按钮。 另一种方法是分离处理程序(但我们不得不放弃使用 lamdas)。

还有其他/更优雅/的方法来打破循环吗?我们能不能把WeakReference贴在这里?

谢谢。

编辑:在这种情况下,按钮甚至不是一个字段。但是还是有循环的,不是吗?它在控制器视图的子视图中。我们必须清除它们吗?我很困惑。

【问题讨论】:

【参考方案1】:

循环在垃圾收集环境中通常不是问题 - 我必须从问题中假设这在单点触控中有所不同?编辑:不,我的假设在这里仍然有效 - 在您链接到它的视频中的 7:50 演示了一个循环被“扫描”丢弃。

周期可以通常在整个周期变得无法到达时被抛弃。这将是一个引用计数系统中的问题。

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

button.TouchUpInside += (sender, e) =>
            ((UIButton)sender).Parent.RemoveFromParentViewController();

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


具体来说就是monotouch的上下文,那么是的,你需要使用WeakReference<T>

var controller = new WeakReference<Puzzle2Controller>(this);
button.TouchUpInside += (sender, e) => 
    var parent = controller.Object;
    if(parent != null) parent.RemoveFromParentViewController();
;

【讨论】:

您好,感谢您的回复。这当然是一种选择,但不幸的是UIViews 没有引用他们的控制器。如果我的理解是正确的,那么 MonoTouch 中的循环 是一个问题,因为底层原生 ObjC 对象使用引用计数。 (见 21:30) 谢谢!这完全有道理。 @MarcGravell:如果循环被“sweep”丢弃,在上述情况下我们还必须使用WeakReference吗?

以上是关于如何修复由 lambda 事件处理程序引起的 GC 周期?的主要内容,如果未能解决你的问题,请参考以下文章

如何修复由 SpringSecurity 引起的 Forbidden 403 错误?

如何修复由 CSS 动画引起的奇怪色带?

如何修复由 react-redux 中的循环依赖引起的“不变违规”

如何修复由目的地引起的膨胀错误不是此 NavGraph 的直接子级

如何修复由 Pycharm 中的 Tensorflow 引起的 cudart64_110.dll 错误?

如何修复由数据类型关系引起的“变量“$_v0_data”得到无效值' - Mutation Resolver