xamarin.ios - 这是 MonoTouch GC 中的错误吗?

标签 xamarin.ios garbage-collection xamarin

Note: I've created a simple project—you can see how switching types between UIButton and CustomButton in storyboard changes GC behavior.



我正试图让我的头脑围绕 MonoTouch 垃圾收集器。
问题类似于 the one fixed in MT 4.0 ,但是具有继承的类型。

为了说明这一点,考虑两个 View Controller ,父级和子级。

subview 包含单个 UIButton即刻写入控制台。
Controller Dispose方法抛出异常,因此很难错过。

这是 subview Controller :
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);
}

父 View Controller 只是呈现子 Controller 并设置一个计时器来关闭它并运行 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() 内部崩溃。从其终结器调用,因为子 Controller 已被垃圾收集。凉爽的。

现在打开 Storyboard并将按钮类型更改为CustomButton . MonoDevelop 将生成一个简单的 UIButton子类:
[Register ("CustomButton")]
public partial class CustomButton : UIButton
{
    public CoolButton (IntPtr handle) : base (handle)
    {
    }

    void ReleaseDesignerOutlets()
    {
    }
}

不知何故将按钮类型更改为 CustomButton足以诱使垃圾收集器认为子 Controller 尚不符合收集条件。

怎么会这样?

最佳答案

这是 MonoTouch(垃圾回收者)不得不生活在引用计数世界(ObjectiveC)中的一个不幸的副作用。

要了解正在发生的事情,需要一些信息:

  • 对于每个托管对象(从 NSObject 派生),都有一个对应的本地对象。
  • 对于自定义托管类(从 UIButton 或 UIView 等框架类派生),托管对象必须保持事件状态,直到 native 对象被释放 [1]。它的工作方式是,当 native 对象的引用计数为 1 时,我们不会阻止托管实例进行垃圾收集。 一旦引用计数增加到 1 以上,我们就会阻止托管实例被垃圾收集。

  • 在您的情况下发生的是一个循环,它穿过 MonoTouch/ObjectiveC 桥,由于上述规则,GC 无法确定可以收集该循环。

    这就是发生的事情:
  • 您的 ChildViewController 有一个 sayHiButton。 native ChildViewController 将保留此按钮,因此其引用计数将为 2(托管 CustomButton 实例持有的一个引用 + native ChildViewController 持有的一个引用)。
  • TouchUpInside 事件处理程序具有对 ChildViewController 实例的引用。

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

    有几种方法可以打破循环来解决这个问题:
  • 当您不再需要事件处理程序时,将其分离。
  • 当您不再需要 ChildViewController 时,将其丢弃。

  • [1] 这是因为托管对象可能包含用户状态。对于镜像对应 native 对象的托管对象(例如托管 UIView 实例),MonoTouch 知道该实例不能包含任何状态,因此只要没有托管代码引用托管实例,GC 就可以收集它。如果稍后需要托管实例,我们只需创建一个新实例。

    关于xamarin.ios - 这是 MonoTouch GC 中的错误吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13058521/

    相关文章:

    c# - 具有透明状态栏的 Xamarin Forms 背景图像

    c# - Xamarin.Forms 选择器默认值

    c# - Xamarin.Essentials Share.RequestAsync() 不适用于 iOS

    xamarin - 无法对 UIImageView 应用多重转换

    c# - MonoTouch.Dialog:UISearchBar 颜色

    java - 删除对对象的引用是否足以避免内存泄漏?

    java - 为什么我的堆会稳定增长并且完全GC从未发生?

    ios - 呈现 UIImagePickerController 使应用程序崩溃

    c# - 如何在 MonoTouch 中绑定(bind)这些?

    java - 有没有办法在 .jar 中包含 VM 参数?