背景:
我有一个标签栏应用程序。每个选项卡都包含导航控制器,允许用户从一个视图转换到另一个视图,显示数据的向下钻取信息(每个视图由视图控制器处理,每个视图控制器类具有didReceiveMemoryWarning
方法)。通过从Web服务中提取数据来填充列表。
问题:
当我使用iPhone模拟器的“硬件>模拟内存警告”选项时,将为所有视图控制器调用didReceiveMemoryWarning
方法 - 甚至是用户正在查看的方法。我不想清除活动视图控制器正在使用的任何内容。我怎样才能做到这一点?
由于内存警告,哪个方法应该在数据发布后重新加载数据? (我看到当用户返回到该视图时,包含表视图的视图控制器类调用viewDidLoad
方法,但如果视图包含(比如UIWebView),则不会调用viewDidLoad
方法。为什么是那?)
已编辑(2009年1月30日星期五 - 03:10 PM)
(注意:我正在使用“界面”构建器来创建视图,并且loadView
方法已被注释掉。)
因此,当视图控制器收到内存警告消息时,这些是执行的步骤:
调用以下方法:
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
调用[super didReceiveMemoryWarning]
后会自动调用[self setView:nil]
吗?
如果要清除任何资源,则应覆盖setView
方法以清除本地资源。
[self setView:nil]
。对? - 我真的很好奇哪种方法可以做出这个决定以及如何做?
请你确认一下。另外,我在遵循此方法时遇到错误,但在控制器类的myObject = nil
方法中释放myObject
后添加dealloc
修复了此问题。感谢。
答案 0 :(得分:12)
这是一个老问题,但我没有看到正确的答案,所以这里是:
当收到内存警告时,在所有视图控制器中调用-didReceiveMemoryWarning
,无论它们是否是“当前”控制器。视图控制器只是监听内存警告事件广播。
如果在内存警告时未使用视图控制器的视图,则控制器将通过将属性设置为nil来卸载它。如何知道视图是否被使用?通过视图的-superview
属性。如果view.superview
为nil,则视图不属于任何树,可以安全卸载。
一旦发生这种情况,控制器的-viewDidUnload
就会被调用。这是卸载任何商店的正确位置,以及将在-viewDidLoad
中重新创建的任何商品。
那么-didReceiveMemoryWarning
是什么?您的控制器可能具有在访问之前不会实例化的对象。例如,您可能有一个控制器,有时需要从文件中获取大量数据,但并非总是如此。您可以为此设置一个属性:
- (NSData*)bigChunkOfData {
// Get data from our instance variable _data, read from disk if necessary
if (_data == nil) {
_data = [[NSData alloc] initWithContentsOfFile:@"/path/to/data"];
}
return _data;
}
这将首次从磁盘读取数据,然后将其保存在实例变量中。由于_data
变量是按需创建的,因此我们可以安全地在低内存情况下卸载它:它将在下次需要时再次创建。
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
[_data release];
_data = nil; // <-- Very important: don't leave strong references dangling.
}
答案 1 :(得分:8)
我这样清理:
-(void)setView:(UIView*)view
{
[super setView:view];
if(view == nil)
{
// Our view has been cleared, therefore we should clean up everything
// we are not currently using
....
如果该视图当前不可见,UIViewController会调用 setView:nil
来响应内存警告 - 这基本上就是您想知道的。
EDITED
回答后续问题:
正确。在UIViewController中实现didReceiveMemoryWarning
就是这样做的。如果你不重写didReceiveMemoryWarning
,那么将调用UIViewController中的基类实现 - 如果你覆盖它,显然你应该调用:
[super didReceiveMemoryWarning]
答案 2 :(得分:1)
关于视图管理和内存警告:
UIKit不仅允许从视图控制器返回导航,还允许从现有视图控制器导航到其他视图控制器。 在这种情况下,将分配一个新的UIViewController,然后加载到视图中。 旧视图控制器将离开屏幕并变为非活动状态,但仍拥有许多对象 - 一些位于自定义属性和变量中,另一些位于视图属性/层次结构中。 新的可见视图控制器在视图对象方面也是如此。
由于移动设备的内存容量有限,拥有两组对象 - 一组在屏幕外视图控制器中,另一组在屏幕视图控制器中 - 可能要处理得太多。
如果UIKit认为有必要,它可以回收一些屏幕外视图控制器的内存,无论如何都没有显示; UIKit知道哪个视图控制器在屏幕上,哪个在屏幕外,毕竟它是管理它们的人(当你调用presentModalViewController:animated:
或dismissModalViewControllerAnimated:
时)。
因此,每次感到压力时,UIKit都会生成一个内存警告,从视图层次结构卸载并释放屏幕外视图,然后调用自定义viewDidUnload方法为您的属性和变量执行相同的操作。
UIKit自动发布self.view,然后允许我们在viewDidUnload代码中手动释放变量和属性。
它适用于所有屏幕外视图控制器。
当系统内存不足时,它会触发didReceiveMemoryWarning
。
屏幕外视图将在内存警告时回收和释放,但您的屏幕视图将不会被释放 - 它是可见且需要的。
如果您的类拥有大量内存,例如缓存,图像等,didReceiveMemoryWarning
就是您应该清除它们的地方,即使它们在屏幕上;否则,您的应用可能会因系统资源过剩而被终止。
你需要覆盖这个方法,以确保你清理你的记忆;只记得你拨打[super didReceiveMemoryWarning];
。
此处提供了更详细的解释:http://myok12.wordpress.com/2010/11/30/custom-uiviewcontrollers-their-views-and-their-memory-management/
答案 3 :(得分:1)
为了确保我不必为我编写的每个视图控制器处理这个问题。我刚刚制作了一个Xcode ViewController模板,该模板提供了关于要释放哪些对象以及何时发布的指南。
此处有更多解释http://iphone2020.wordpress.com/2010/05/30/efficient-memory-handling-in-uiviewcontroller-part-1/
希望它有用。
答案 4 :(得分:0)
幸运的是,模拟器有一个方便的功能,允许您将低内存情况置于测试中。在viewDidLoad和didReceiveMemoryWarning中放置一些NSLog()语句,如下所示: 
- (void)viewDidLoad {
NSLog(@"viewDidLoad");
...
}
- (void)didReceiveMemoryWarning {
NSLog(@"didReceiveMemoryWarning");
}