【问题标题】:UIViewController prevent view from unloadingUIViewController 防止视图卸载
【发布时间】:2011-02-27 06:37:47
【问题描述】:

当我的 iPhone 应用程序收到内存警告时,当前不可见的 UIViewController 视图会被卸载。在一个特定的控制器中,卸载视图和出口是相当致命的。

我正在寻找一种方法来防止此视图被卸载。我发现这种行为相当愚蠢 - 我有一个缓存机制,所以当内存警告出现时 - 我卸载了自己的大量数据并释放了足够的内存,但我绝对需要保持这个视图不变。

我看到 UIViewController 有一个方法unloadViewIfReloadable,当内存警告出现时会调用它。有人知道如何告诉 Cocoa Touch 我的视图不可重新加载吗?

任何其他建议如何防止我的视图在内存警告中被卸载?

提前致谢


关于视图控制器的视图生命周期的 Apple 文档说:

didReceiveMemoryWarning - 默认 实现只发布视图 如果它确定这样做是安全的 所以

现在...我用一个空函数覆盖didReceiveMemoryWarning,它只调用 NSLog 让我知道收到了警告。但是 - 视图无论如何都会被卸载。另外,究竟是根据什么标准决定一个视图是否可以安全卸载......哦!这么多问题!

【问题讨论】:

  • 也许你应该重构你的设计,这样不能被释放的部分是一个单独的持久对象的一部分,而不是视图本身的一部分。
  • 嗨大卫,屏幕上显示了整个视图层次结构,我不想将它拆掉然后重新构建它,而我在顶部显示一个模态视图控制器......不是这不是矫枉过正吗?
  • 我喜欢 @umpo 提供的解决方案,但是伙计们,这段代码会导致像 MyViewController implementation of -viewDidUnload caused the view to be reloaded. This will adversely impact system performance. 这样的运行时警告 - 你只是忽略它还是你做了什么?
  • @delirus 我有 3 个应用程序已经在使用 @umpo 发布的确切代码,但我没有收到这样的警告,我正在向 4.3 发展
  • 嗯,一定是我。谢谢你的回复:)

标签: ios cocoa-touch memory-management uiview uiviewcontroller


【解决方案1】:

根据文档,didReceiveMemoryWarning: 的默认实现会在安全的情况下释放视图(即:superview==nil)。

为了防止视图被释放,您可以覆盖 didReceiveMemoryWarning: 但在您的实现中不要调用 [super didReceiveMemoryWarning]。这是默认释放视图的位置(如果不可见)。

默认的 didReceiveMemoryWarning 通过调用 [viewcontroller setView:nil] 来释放视图,因此您可以覆盖它。

【讨论】:

  • 覆盖 didReceiveMemoryWarning 对我没有任何影响。覆盖 setView 会使调试控制台抱怨一大堆关于它将如何对性能产生不利影响的问题。这会让你远离应用商店吗?
【解决方案2】:

似乎对我有用的是覆盖setView: 以忽略设置为零。这很笨拙,但是,这是一个笨拙的问题,这成功了:

-(void)setView:(UIView*)view {
    if(view != nil || self.okayToUnloadView) {
        [super setView:view];
    }
}

【讨论】:

  • 哇...这就是我所说的“跳出框框思考”,非常感谢,当我推出我的应用程序的下一次更新时,我会实现它
  • 只是一个通知 - 我实现了这个 - 3 个应用程序现在在 App Store 上使用它,对我来说解决方案工作正常
  • 这个问题是 viewDidUnload 方法仍然被调用,如果你不小心可能会混淆你的代码。我怀疑这就是为什么有些人会看到有关不良表现的警告。
  • 你能否提供更多关于你使用self.okayToUnloadView的细节?
【解决方案3】:

可以这么简单吗?

尽管文档中没有提到这一点,但似乎如果我只在 viewDidLoad 中保留我的视图,那么它不会在内存警告中被释放。我在模拟器中尝试了几个连续的警告,但一切似乎都很好。

所以...目前的诀窍是在 viewDidLoad 中“保留”,在 dealloc 中释放 - 这样视图控制器会“卡在”视图中,直到需要释放。

我会再测试一些,并写下结果

【讨论】:

  • 这个技巧似乎对我不起作用。直接调用 Dealloc,因此无论如何都会卸载视图。我也尝试使用 [self retain];这只是让它无休止地尝试解除分配。不知道为什么。
【解决方案4】:

我认为这些想法都行不通。我尝试覆盖 [didReceiveMemoryWarning],这适用于某些手机,但发现一部手机在调用该方法之前卸载了视图(一定是内存极低或其他原因)。覆盖 [setView] 会产生大量日志警告,因此我不会冒险让 Apple 冒险。保留视图只会泄漏该视图 - 它会防止崩溃,但不会真正起作用 - 下次加载控制器 UI 时,视图将被替换。

所以实际上,您只需要计划在视图离开屏幕时随时将其卸载,这并不理想,但您就可以了。我发现使用此功能的最佳模式是立即提交,因此您的 UI 始终是最新的,或者复制-编辑-复制,您将模型复制到临时实例,填充您的视图并使用立即提交该实例,然后在用户点击“保存”或其他任何操作时将更改复制回您的原始模型。

【讨论】:

  • 对我来说,重写 setView: 方法效果很好,不会产生警告,而且我在 App Store 上已经有 3 个使用这种技术的应用程序
  • 不幸的是,当模式视图被关闭以显示底层的空视图时,它无法解决空白的白屏问题。
【解决方案5】:

由于已接受的解决方案存在问题,即使视图被阻止清除,viewDidUnload 仍会被调用,因此我使用了一种不同但仍然脆弱的方法。系统使用unloadViewForced: 消息将视图卸载到控制器,因此我将拦截该消息以阻止该消息。这可以防止对viewDidUnload 的混淆调用。代码如下:

@interface UIViewController (Private)
- (void)unloadViewForced:(BOOL)forced;
@end

- (void)unloadViewForced:(BOOL)forced {
    if (!_safeToUnloadView) {
        return;
    }
    [super unloadViewForced:forced];
}

这有明显的问题,因为它在 UIViewController 中截获了一条未记录的消息。

progrmr 在上面发布了一个答案,建议改为拦截 didReceiveMemoryWarning。根据我看到的堆栈跟踪,拦截也应该有效。我还没有尝试过这条路线,因为我担心可能还有其他内存清理也会被阻止(例如导致它不调用带有内存警告消息的子视图控制器)。

【讨论】:

  • 您在哪里为UIViewController 指定类别?在.m 文件中?出于某种原因,我重载的unloadViewForced 永远不会被调用。
  • 是的,在 .m 文件中。
  • 我没有检查它是否停止与任何最近的 iOS 更新一起工作。由于它是一个未记录的 API,它可能已经消失或被重命名。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-30
  • 2011-01-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-01
相关资源
最近更新 更多