【问题标题】:Release/dispose of a UIViewController in MonoTouch在 MonoTouch 中释放/处置 UIViewController
【发布时间】:2023-04-10 00:49:01
【问题描述】:

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

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

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

【问题讨论】:

    标签: objective-c uiviewcontroller garbage-collection xamarin.ios didreceivememorywarning


    【解决方案1】:

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

    不完全是。 MonoTouch 托管实例将保留对本机实例的引用。只要 ma​​naged 实例存在,native 实例就会存在(因为它们是引用计数的,MonoTouch 不会释放它的引用)。

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

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

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

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

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

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

    怀疑您正在处置(手动或非手动)您的托管实例,然后它们完成工作。以下是可能发生的情况的简化

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

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

    【讨论】:

    • 谢谢,但我确定我没有任何参考资料了。引用VC2 的唯一托管事物是我的VC1 实例,所以当我清空这个实例时,本机VC2 实例上的引用计数应该为0。但似乎iOS 保留了对用于分发didReceiveMemoryWarning 消息的控制器,因为一旦有didReceiveMemoryWarning 消息,我的应用程序就会崩溃。
    • 您可能没有 托管 引用,但您很可能还剩下 本机 引用(在它们或它们的父级之间)。在确定它们的生命周期(本机端)结束之前,不应将它们(托管端)清空,因为它可能会回调到已处置(托管)的实例中。如果您有(或可以提取)一个小测试用例,请打开错误报告@bugzilla.xamarin.com,我们将审查代码。
    • 好的,我正在尝试隔离它。感谢您的帮助。
    • @Sebastien,是否可以确定哪个选择器系统尝试从 sigsegv 崩溃日志中调用?
    • @chebum 现在不是(本机堆栈不可见),但它正在处理中
    【解决方案2】:

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

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

    您也可以在某些根 VC 的 DidReceiveMemoryWarning 方法中调用 GC.Collect

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-12
      • 1970-01-01
      • 2016-12-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多