iPhone开发 - 模拟内存警告

时间:2009-01-29 10:00:25

标签: iphone memory warnings simulator

背景

我有一个标签栏应用程序。每个选项卡都包含导航控制器,允许用户从一个视图转换到另一个视图,显示数据的向下钻取信息(每个视图由视图控制器处理,每个视图控制器类具有didReceiveMemoryWarning方法)。通过从Web服务中提取数据来填充列表。

问题

当我使用iPhone模拟器的“硬件>模拟内存警告”选项时,将为所有视图控制器调用didReceiveMemoryWarning方法 - 甚至是用户正在查看的方法。我不想清除活动视图控制器正在使用的任何内容。我怎样才能做到这一点?

由于内存警告,哪个方法应该在数据发布后重新加载数据? (我看到当用户返回到该视图时,包含表视图的视图控制器类调用viewDidLoad方法,但如果视图包含(比如UIWebView),则不会调用viewDidLoad方法。为什么是那?)

已编辑(2009年1月30日星期五 - 03:10 PM)

(注意:我正在使用“界面”构建器来创建视图,并且loadView方法已被注释掉。)

因此,当视图控制器收到内存警告消息时,这些是执行的步骤:

  1. 调用以下方法:

    - (void)didReceiveMemoryWarning {
        [super didReceiveMemoryWarning]; 
    }
    
  2. 调用[super didReceiveMemoryWarning]后会自动调用[self setView:nil]吗?

  3. 如果要清除任何资源,则应覆盖setView方法以清除本地资源。

  4. 如果视图当前处于活动状态(默认情况下),则不会调用
  5. [self setView:nil]。对? - 我真的很好奇哪种方法可以做出这个决定以及如何做?

  6. 请你确认一下。另外,我在遵循此方法时遇到错误,但在控制器类的myObject = nil方法中释放myObject后添加dealloc修复了此问题。感谢。

5 个答案:

答案 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

回答后续问题:

  1. 正确。
  2. 这就是我的工作,对我有用。
  3. 正确。在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");
}