5

据我了解,在 MonoTouch 中处理 Cocoa 对象时,我们需要保存对它们的引用。这样做的原因是 ObjC 运行时可能仍然持有对对象的引用,如果我们在它们上没有“MonoTouch 引用”,它们可能会被垃圾收集,这会导致EXC_BAD_ACCESS一旦 ObjC 运行时尝试访问它们。

比如说,我们有两个 UIViewController 子类,VC1 和 VC2。如果用户单击 VC1 上的按钮,UI 会导航到 VC2,并且用户可以来回导航。如果我每次用户导航到它时都创建一个新的 VC2 实例,那么对旧实例的引用会丢失,因此它们会被垃圾收集,并且应用程序在下次 adidReceiveMemoryWarning传播到 UIViewControllers 时崩溃。

我怎样才能释放旧的引用,所以我不必每次都使用相同的 VC2 实例?Dispose似乎还不够。

4

2 回答 2

6

据我了解,在 MonoTouch 中处理 Cocoa 对象时,我们需要保存对它们的引用。

不完全的。MonoTouch托管实例将保留对本机实例的引用。只要托管实例存在,本机实例就会处于活动状态(因为它们被引用计数并且 MonoTouch 不会释放它的引用)。

IOW 您需要保留对 MonoTouch托管实例的引用,只要它们的本机部分是必需的。

这样做的原因是 ObjC 运行时可能仍然持有对对象的引用......它们可能被垃圾收集,

本机(Objective C)实例是引用计数的,而不是垃圾收集的。本地实例在其引用计数达到 0之前不会被释放(当关联的托管实例存在时不会发生这种情况);

本机实例也可以保存对其他本机实例的引用。并非每个本机实例都有相应的托管实例。

一旦 ObjC 运行时尝试访问它们,就会导致 EXC_BAD_ACCESS。

这不会发生,至少不会这样。OTOH 很难告诉您您的情况发生了什么(没有看到代码和/或崩溃)。

怀疑您在托管实例完成工作之前正在处理(手动或不手动)您的托管实例。以下是可能发生的情况的简化:

  • 您创建一个托管MT.X 实例(例如一个UIView);
  • 这将创建并引用本机X(本机引用计数 == 1);
  • 您在 MT.X 上覆盖一个事件(或添加一个委托......)“ViewWillUnload”(它本身也存在);
  • 您将 MT.X 实例分配给另一个(托管)实例,例如UIViewController;
  • UIViewController将添加对本X 的引用(本机引用计数 == 2);
  • 应用程序执行愉快...
  • 您停止引用该MT.X实例(例如将变量设置为null或不同的实例);
  • 由于不再引用MT.X垃圾收集器将处理托管实例,调用Dispose这将减少本机X 的引用(本机引用计数 == 1)。但是本实例不会被释放,因为它仍然被视图控制器引用(不是 0);
  • UIViewController做一些本机触发的事情X.ViewWillUnload(例如,它试图加载一个新的UIView);
  • 因为X仍然存在于本机(ref count == 1),它会调用它ViewWillUnload,它会尝试回到托管实例......被释放

此问题的解决方案是确保在托管实例的本机部分完成其工作之前,您不会处置托管实例。

于 2012-07-23T13:01:43.833 回答
1

我在我的应用程序中遇到了同样的情况,GC 正确地收集了对象。换句话说,我从来没有遇到过简单地取消对 VC 的引用并让 GC 完成其余工作的问题。

但是,当像您一样调用Dispose方法时,我确实遇到了问题。看来我们不应该手动执行此操作。相反,我们应该等待 GC 收集对象并释放其资源。基NSObject类在其终结器中有一个调用Dispose,因此所有非托管资源将在对象被收集时被释放。

你也可以调用GC.Collect一些根VC的DidReceiveMemoryWarning方法。

于 2012-07-23T12:34:53.963 回答