5

背景

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

问题

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

由于内存警告而释放数据后,哪种方法应该具有重新加载数据的实现?viewDidLoad(当用户返回该视图时,我看到包含表视图调用方法的视图控制器类,但如果视图包含(例如 UIWebView)viewDidLoad则不调用方法。为什么会这样?)

已编辑(2009 年 1 月 30 日星期五 - 下午 3:10)

(注意:我使用 Interface builder 来创建视图,并且loadView方法被注释掉了。)

因此,当视图控制器收到内存警告消息时,将执行以下步骤:

  1. 调用以下方法:

    - (void)didReceiveMemoryWarning {
        [super didReceiveMemoryWarning]; 
    }
    
  2. 作为调用的结果[super didReceiveMemoryWarning][self setView:nil]被自动调用?

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

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

请你确认一下。另外,我在遵循这种方法时遇到了一个错误,但是在控制器类的方法中myObject = nil发布后添加解决了这个问题。谢谢。myObjectdealloc

4

5 回答 5

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.
}
于 2011-05-05T03:43:56.687 回答
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
....

setView:nil由 UIViewController 调用以响应内存警告,如果该视图当前不可见 - 这基本上是您想知道的。

已编辑

在回答后续行动:

  1. 正确的。
  2. 这就是我所做的,它对我有用。
  3. 正确的。didReceiveMemoryWarningUIViewController 中的实现就是这样做的。如果你不覆盖didReceiveMemoryWarning,那么 UIViewController 中的基类实现将被调用——如果你覆盖它,显然你应该调用:

    [super didReceiveMemoryWarning]
    
于 2009-01-29T10:31:20.933 回答
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/

于 2010-12-01T03:48:50.363 回答
1

为了确保我不必为我编写的每一个视图控制器都处理这个问题。我刚刚制作了一个 Xcode ViewController 模板,它提供了关于释放哪些对象以及何时释放的指南。

更多解释在这里http://iphone2020.wordpress.com/2010/05/30/efficient-memory-handling-in-uiviewcontroller-part-1/

希望它有用。

于 2011-01-29T05:28:01.200 回答
0

幸运的是,模拟器有一个方便的功能,可以让您测试内存不足的情况。在 viewDidLoad 和 didReceiveMemoryWarning 中都放一些 NSLog() 语句,像这样:</p>

- (void)viewDidLoad {
    NSLog(@"viewDidLoad"); 
    ...
}

- (void)didReceiveMemoryWarning {
    NSLog(@"didReceiveMemoryWarning");
}
于 2014-05-29T16:05:24.117 回答