代码之家  ›  专栏  ›  技术社区  ›  Mustafa

iPhone开发-模拟内存警告

  •  5
  • Mustafa  · 技术社区  · 17 年前

    背景 :

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

    问题 :

    当我使用iPhone模拟器的“Hardware>Simulate Memory Warning”选项时, 没有收到记忆警告 方法是为所有视图控制器调用的,甚至是用户正在查看的控制器。我不想清除活动视图控制器正在使用的任何内容。我怎样才能做到?

    在由于内存警告而释放数据后,哪个方法应该具有重新加载数据的实现?(我看到包含表视图调用的视图控制器类 viewDidLoad 方法,但如果视图包含(例如uiWebView),则 可视负载 未调用方法。为什么?)

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

    (注意:我正在使用Interface Builder创建视图,以及 loadView 方法被注释掉。)

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

    1. 调用以下方法:

      - (void)didReceiveMemoryWarning {
          [super didReceiveMemoryWarning]; 
      }
      
    2. 由于呼叫 [super didReceiveMemoryWarning] , [self setView:nil] 自动呼叫?

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

    4. [自我设置视图:无] 如果视图当前处于活动状态(默认情况下),则不调用。正确的?-我真的很好奇是哪种方法做了这个决定,又是怎么做的?

    你能确认一下吗?另外,按照这种方法我得到了一个错误,但是 myObject = nil 释放后 myObject 在里面 dealloc 控制器类的方法修复了该问题。谢谢。

    5 回复  |  直到 11 年前
        1
  •  12
  •   xaethos    14 年前

    这是一个古老的问题,但我看不到一个正确的答案,所以下面是:

    当收到内存警告时, -didReceiveMemoryWarning 在所有视图控制器中调用,无论它们是否为“当前”视图控制器。视图控制器只是在监听内存警告事件广播。

    如果在内存警告时视图控制器的视图未被使用,则控制器将通过将属性设置为nil来卸载它。它如何知道视图是否被使用?在视野中 -superview 财产。如果 view.superview 为零,视图不是任何树的一部分,可以安全卸载。

    一旦发生这种情况,控制器 -viewDidUnload 调用。这是卸载任何插座以及在其中重新创建的任何插座的正确位置。 -viewDidLoad .


    那么什么是 -没有收到记忆警告 为了?您的控制器可能具有在访问之前不会实例化的对象。例如,您可以有一个控制器,它有时需要一个文件中的大数据块,但并不总是如此。您可以这样设置它的属性:

    - (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
  •  8
  •   Airsource Ltd    13 年前

    我会这样清理:

    -(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. 对的。实施 didReceiveMemoryWarning 在uiviewController中是这样做的。如果你不重写 没有收到记忆警告 ,然后将调用uiviewController中的基类实现-如果确实要重写它,显然应该调用:

      [super didReceiveMemoryWarning]
      
        3
  •  1
  •   Bharath    15 年前

    为了确保我不必为我写的每个视图控制器都处理这个问题。我刚刚制作了一个xcode viewcontroller模板,它提供了关于哪些对象要发布以及何时发布的指导原则。

    此处提供更多说明 http://iphone2020.wordpress.com/2010/05/30/efficient-memory-handling-in-uiviewcontroller-part-1/

    希望它有用。

        4
  •  1
  •   Ohad Kravchick    13 年前

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

    uikit不仅允许从视图控制器返回导航,还允许从现有视图控制器导航到其他视图控制器。 在这种情况下,将分配一个新的uiviewcontroller,然后加载到视图中。 旧的视图控制器将离开屏幕并变为非活动状态,但仍拥有许多对象,其中一些在自定义属性和变量中,另一些在视图属性/层次结构中。 新的可视视图控制器也同样适用于其视图对象。

    由于移动设备的内存有限,拥有这两组对象“一个在屏幕外视图控制器中,另一个在屏幕上视图控制器中”可能太多无法处理。 如果uikit认为有必要,它可以回收一些屏幕外视图控制器的内存,这些内存无论如何都不会显示;uikit知道哪些视图控制器在屏幕上,哪些是屏幕外的,毕竟,它是管理它们的那个(当您调用 presentModalViewController:animated: dismissModalViewControllerAnimated: ) 因此,每当感到压力时,uikit都会生成一个内存警告,从视图层次结构中卸载并释放屏幕外视图,然后调用自定义的viewdidUnload方法,以便对属性和变量执行相同的操作。 uikit自动释放self.view,然后允许我们手动释放viewdidUnload代码中的变量和属性。 它适用于所有非屏幕视图控制器。

    当系统内存不足时,它将触发 didReceiveMemoryWarning . 屏幕外视图将在内存警告时被回收和释放,但屏幕上视图将不会被释放“它是可见的和需要的”。 如果类拥有大量内存,例如缓存、图像等, 没有收到记忆警告 是您应该清除它们的地方,即使它们在屏幕上;否则,您的应用程序可能会因系统资源过剩而终止。 您需要重写此方法以确保清理内存;只需记住您调用了 [super didReceiveMemoryWarning]; .

    这里有一个更详细的解释: http://myok12.wordpress.com/2010/11/30/custom-uiviewcontrollers-their-views-and-their-memory-management/

        5
  •  0
  •   Ricardo    11 年前

    幸运的是,模拟器有一个方便的功能,允许您将内存不足的情况放到测试中。在viewDidLoad和didReceiveMemoryWarning中放置一些nslog()语句,如下所示: 伊米尔

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