UIViewController阻止视图卸载

时间:2010-06-03 11:40:27

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

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

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

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

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

提前致谢


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

  

didReceiveMemoryWarning - 默认值   实现仅释放视图   如果它确定它是安全的   所以

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

5 个答案:

答案 0 :(得分:15)

根据文档,didReceiveMemoryWarning:的默认实现会释放视图,如果它是安全的(即:superview == nil)。

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

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

答案 1 :(得分:13)

似乎对我有用的是覆盖setView:以忽略设置为nil。这很愚蠢,但是,这是一个麻烦的问题,这就是诀窍:

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

答案 2 :(得分:1)

可以这么简单吗?

即使文档中没有提到这一点,但似乎如果我在viewDidLoad中专门保留我的视图,那么它就不会在Memory Warning上发布。我在模拟器中尝试了几个连续的警告,但仍然看起来很好。

所以...目前的诀窍是在viewDidLoad中“保留”,在dealloc中发布一个版本 - 这样viewcontroller就会被视图“卡住”,直到需要释放它为止。

我会测试更多,并写下结果

答案 3 :(得分:1)

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

所以你真的只需要计划你的视图在屏幕外的任何时候被卸载,这不是理想的,但你去了。我发现使用它的最佳模式是立即提交,因此您的UI始终是最新的,或者是copy-edit-copy,您可以将模型复制到临时实例,填充视图并使用立即提交该实例,然后在用户点击“保存”或其他任何内容时将更改复制回原始模型。

答案 4 :(得分:1)

因为即使视图被阻止被清除,所接受的解决方案仍有viewDidUnload被调用的问题,我使用的是另一种但仍然很脆弱的方法。系统使用unloadViewForced:消息将视图卸载到控制器,因此我拦截了阻止消息的消息。这可以防止对viewDidUnload的混淆调用。这是代码:

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

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

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

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