这是MonoTouch的GC的错误吗?(Is this a bug in MonoTouch GC?

2019-06-18 14:54发布

注: 我创建了一个简单的项目 -你可以看到开关之间类型UIButtonCustomButton在故事板改变GC的行为。

我试图让我的头缠着MonoTouch的垃圾收集器。
这个问题类似于一个固定在MT 4.0 ,但与遗传类型。

为了说明它,考虑两个视图控制器,父母和孩子。

孩子的视图包含一个单一UIButton写入到自来水控制台。
控制器的Dispose ,所以很难错过方法抛出异常。

这里有云子视图控制器:

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

    sayHiButton.TouchUpInside += (sender, e) =>
        SayHi();
    }
}

void SayHi()
{
    Console.WriteLine("Hi");
}

protected override void Dispose (bool disposing)
{
    throw new Exception("Hey! I've just been collected.");
    base.Dispose (disposing);
}

父视图控制器只呈现子控制器并设置定时器驳回并运行GC:

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

    var child = (ChildViewController)Storyboard.InstantiateViewController("ChildViewController");

    NSTimer.CreateScheduledTimer(2, () => {
        DismissViewController(false, null);
        GC.Collect();
    });

    PresentViewController(child, false, null);
}

如果你运行这段代码,它可预见的内部崩溃ChildViewController.Dispose()从它的终结称呼,是因为孩子控制器已经被垃圾收集。 凉。

现在打开故事板和更改按钮类型CustomButton 。 MonoDevelop中会生成一个简单UIButton子类:

[Register ("CustomButton")]
public partial class CustomButton : UIButton
{
    public CoolButton (IntPtr handle) : base (handle)
    {
    }

    void ReleaseDesignerOutlets()
    {
    }
}

不知怎的,改变按钮类型CustomButton足以欺骗垃圾收集,以为孩子控制器不符合回收。

这是怎么回事呢?

Answer 1:

这是MonoTouch的具有生活在一个参考计数世界(的ObjectiveC)一个不幸的副作用(被垃圾收集谁)。

还有的是能够理解发生了什么事情需要一些信息:

  • 对于每一个管理对象(从NSObject的派生),有一个相应的天然对象。
  • 对于自定义管理类(从框架类如UIButton的或衍生的UIView),直到本机对象被释放[1]被管理对象必须保持活着。 它的工作方式是,当一个本地对象有1引用计数,我们不会阻止管理的实例,从得到垃圾收集。 只要引用计数增加超过1,我们阻止越来越垃圾回收的管理实例。

会发生什么事,你的情况是一个循环,横跨的MonoTouch /的ObjectiveC桥梁,由于上述规则,GC无法确定该周期可以被收集。

这是发生了什么:

  • 你ChildViewController有sayHiButton。 天然ChildViewController将会保留这个按钮,这样它的引用计数将是2(由被管理的CustomButton实例+一个由本地ChildViewController保持的基准保持一个参考)。
  • 该TouchUpInside事件处理程序的ChildViewController实例的引用。

现在你看到的CustomButton实例不会被释放,因为它的引用计数为2而ChildViewController实例不会被释放,因为的CustomButton的事件处理程序对它的引用。

有一对夫妇的方式来打破这个恶性循环,以解决这个问题:

  • 拆下事件处理程序,当你不再需要它。
  • 处置ChildViewController当你不再需要它。

[1]这是因为被管理对象可以包含用户的状态。 对于被镜像对应的天然对象(如被管理的UIView实例)的MonoTouch知道该实例不能包含任何状态,所以只要没有托管代码具有对管理实例的引用管理对象中,GC可以收集它。 如果管理的实例,需要在后面的阶段,我们只是创建一个新的。



文章来源: Is this a bug in MonoTouch GC?