注意:I've created a simple project - 您可以看到故事板中
UIButton
和CustomButton
之间切换类型如何改变GC行为。
我正试图把头部缠绕在MonoTouch垃圾收集器上 该问题类似于the one fixed in 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
足以诱骗垃圾收集器认为子控制器尚不具备收集资格。
怎么回事?
答案 0 :(得分:45)
这是一个不幸的副作用MonoTouch(谁是垃圾收集)必须住在一个参考计数世界(ObjectiveC)。
需要一些信息才能了解正在发生的事情:
在你的情况下会发生一个循环,它穿过MonoTouch / ObjectiveC桥,由于上述规则,GC无法确定是否可以收集循环。
这就是:
现在您看到CustomButton实例将不会被释放,因为它的引用计数为2.并且不会释放ChildViewController实例,因为CustomButton的事件处理程序具有对它的引用。
有几种方法可以打破这个周期来解决这个问题:
[1]这是因为托管对象可能包含用户状态。对于镜像相应本机对象(例如托管UIView实例)的托管对象,MonoTouch知道实例不能包含任何状态,因此只要没有托管代码具有对托管实例的引用,GC就可以收集它。如果稍后需要托管实例,我们只需创建一个新实例。