Note: I've created a simple project—you can see how switching types between
UIButton
andCustomButton
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)中的一个不幸的副作用。
要了解正在发生的事情,需要一些信息:
在您的情况下发生的是一个循环,它穿过 MonoTouch/ObjectiveC 桥,由于上述规则,GC 无法确定可以收集该循环。
这就是发生的事情:
现在您看到 CustomButton 实例不会被释放,因为它的引用计数是 2。而且 ChildViewController 实例不会被释放,因为 CustomButton 的事件处理程序有对它的引用。
有几种方法可以打破循环来解决这个问题:
[1] 这是因为托管对象可能包含用户状态。对于镜像对应 native 对象的托管对象(例如托管 UIView 实例),MonoTouch 知道该实例不能包含任何状态,因此只要没有托管代码引用托管实例,GC 就可以收集它。如果稍后需要托管实例,我们只需创建一个新实例。
关于xamarin.ios - 这是 MonoTouch GC 中的错误吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13058521/