当我的iPhone应用程序收到内存警告时,当前不可见的UIViewControllers的视图将被卸载。在一个特定的控制器中,卸载视图和插座是相当致命的。
我正在寻找一种方法来防止这个视图被卸载。我发现这个行为相当愚蠢 - 我有一个缓存机制,所以当一个内存警告来临时 - 我卸载了大量的数据并且我释放了足够的内存,但我绝对需要这个视图不受影响。
我看到UIViewController有一个方法unloadViewIfReloadable
,当内存警告到来时会被调用。有人知道如何告诉Cocoa Touch我的视图无法重新加载吗?
如何阻止我的视图在内存警告中被卸载的任何其他建议?
提前致谢
关于视图控制器的视图生命周期的Apple文档说:
didReceiveMemoryWarning - 默认值 实现仅释放视图 如果它确定它是安全的 所以
现在......我用一个只调用NSLog的空函数覆盖didReceiveMemoryWarning
,让我知道收到了警告。但是 - 无论如何都要卸载视图。另外,根据什么标准确定视图是否可以安全卸载......哦!这么多问题!
答案 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
。根据我看到的堆栈跟踪,拦截也应该有效。我没有尝试过这条路线,因为我担心可能会有其他内存清理也会被阻止(例如导致它不能通过内存警告消息调用子视图控制器)。