我们在过去的8个月里一直在使用Xamarin iOS,并开发了一个具有许多屏幕,功能和嵌套控件的非平凡企业应用程序.我们已经完成了我们自己的MVVM拱门,跨平台BLL和DAL为"推荐".我们在Android之间共享代码,甚至我们的BLL/DAL也在我们的网络产品上使用.
一切都很好,除了现在在项目的发布阶段,我们在基于Xamarin iOS的应用程序中发现无法修复的内存泄漏.我们已经遵循了所有"指南"来解决这个问题,但实际情况是C#GC和Obj-C ARC似乎是不兼容的垃圾收集机制,它们以当前的方式在monotouch平台中相互叠加.
我们发现现实情况是,原生对象和管理对象之间的硬周期WILL发生,FREQUENTLY对于任何不平凡的应用程序.在您使用lambdas或手势识别器的任何地方都可以轻松实现这一点.加上MVVM的复杂性,这几乎是一种保证.只想念其中一种情况,并且永远不会收集整个对象图.这些图表将引诱其他物体进入并像癌症一样成长,最终导致iOS的迅速和无情的消灭.
Xamarin的答案是对这个问题的不感兴趣的推迟以及"开发者应该避免这些情况"的不切实际的期望.仔细考虑这一点就可以看出这是对垃圾收集在Xamarin中基本被打破的承认.
现在对我的认识是,在传统的c#.NET意义上,你并没有真正在Xamarin iOS中获得"垃圾收集".您需要使用"垃圾维护"模式实际上让GC移动并完成其工作,即使这样,它也永远不会是完美的 - 非决定性的.
我的公司投入了大量资金,试图阻止我们的应用程序崩溃和/或内存不足.我们基本上必须明确地和递归地处理每一件该死的东西,并在应用程序中实施垃圾维护模式,只是为了阻止崩溃并拥有我们可以销售的可行产品.我们的客户是支持和宽容的,但我们知道这不可能永远存在.我们希望Xamarin有一个专门的团队来处理这个问题,并且一劳永逸地得到它.不幸的是,看起来不像.
问题是,我们的经验是用Xamarin编写的非平凡企业级应用程序的例外或规则吗?
UPDATE
请参阅DisposeEx方法和解决方案的答案.
注意:我创建了一个简单的项目 - 您可以看到故事板之间
UIButton和CustomButton故事板中的切换类型如何改变GC行为.
我正试图让我的脑袋缠绕着MonoTouch垃圾收集器.
该问题类似于MT 4.0中修复的问题,但是使用了继承类型.
为了说明它,请考虑两个视图控制器,父级和子级.
子视图包含一个UIButton可以随时写入控制台的视图.
Controller的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);
}
Run Code Online (Sandbox Code Playgroud)
父视图控制器只显示子控制器并设置一个计时器来关闭它并运行GC:
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
var child = (ChildViewController)Storyboard.InstantiateViewController("ChildViewController");
NSTimer.CreateScheduledTimer(2, () => {
DismissViewController(false, null);
GC.Collect();
}); …Run Code Online (Sandbox Code Playgroud) 我想我已经接近了解Mono GC和ObjC如何重新计算在一起生活.
它的工作方式是当本机对象的引用计数为1时,我们不会阻止托管实例进行垃圾回收.一旦引用计数增加到1以上,我们就会阻止托管实例收集垃圾.
这是因为托管对象可能包含用户状态.对于镜像相应本机对象(例如托管UIView实例)的托管对象,MonoTouch知道实例不能包含任何状态,因此只要没有托管代码具有对托管实例的引用,GC就可以收集它.如果稍后需要托管实例,我们只需创建一个新实例.
因此,如果我创建了一个CustomButton继承UIButton,将其作为子视图添加到我的View,让托管引用滑出范围然后运行GC,此托管 CustomButton仍然不符合收集条件.
为什么不能收集?当然它可能具有类似属性的托管状态,但是如果没有来自托管对象的链接,谁关心这个状态?它可能也只是消失,为什么不能呢?
我正在考虑一个可能的原因:订阅CustomButton事件不会使GC保持活动状态,因此当收集对象时,事件会停止触发.这可能会导致意外行为.
它是否正确?是否有其他原因使托管对象保持活动状态,即使没有人链接它?