【问题标题】:iPhone Development - Simulate Memory WarningiPhone 开发 - 模拟内存警告
【发布时间】:2010-10-04 05:08:10
【问题描述】:

背景

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

问题

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

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

已编辑(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](默认情况下)。正确的? - 我真的很好奇哪种方法做出这个决定以及如何做出决定?

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

【问题讨论】:

    标签: iphone memory warnings simulator


    【解决方案1】:

    这是一个老问题,但我没有看到正确的答案,所以这里是:

    当收到内存警告时,-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.
    }
    

    【讨论】:

      【解决方案2】:

      我是这样清理的:

      -(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. 正确。 UIViewController 中didReceiveMemoryWarning 的实现就是这样做的。如果你不覆盖didReceiveMemoryWarning,那么 UIViewController 中的基类实现将被调用 - 如果你确实覆盖它,显然你应该调用:

        [super didReceiveMemoryWarning]
        

      【讨论】:

      • 即使我不重写 didReceiveMemoryWarning 方法,我的视图也会被清除。这是为什么呢?
      • 不覆盖意味着你得到了 UIViewController 实现的默认行为,即清除你的视图。如果您的覆盖只是调用超级实现,那与根本没有覆盖一样。
      【解决方案3】:

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

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

      希望它有用。

      【讨论】:

        【解决方案4】:

        关于视图管理和内存警告:

        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/

        【讨论】:

          【解决方案5】:

          幸运的是,模拟器有一个方便的功能,可以让您测试内存不足的情况。在 viewDidLoad 和 didReceiveMemoryWarning 中添加一些 NSLog() 语句,如下所示: 

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2013-01-20
            • 1970-01-01
            • 2011-09-05
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-03-23
            相关资源
            最近更新 更多