【问题标题】:Questions about memory warnings关于内存警告的问题
【发布时间】:2013-09-05 08:25:15
【问题描述】:

我的 iOS 应用存在内存问题,对此我有几个问题。

首先,我正在使用 iOS 6 并且正在使用 ARC。

现在让我解释一下我的情况:

我有 2 个视图。从第一个视图中,如果我点击一个按钮,我会创建第二个视图(使用 allocinit)并使用以下代码将其显示为模态:

[self presentViewController:secondView animated:YES completion:^{
        [secondView prepareToDraw];   // Function I use to start my computations and rendering
}];

有时,当计算完成时,我想关闭我的第二个视图并返回到第一个视图。我从第二个角度使用此代码:

[self dismissViewControllerAnimated:YES completion:^{
            [self finished];  // Function I use to free some malloc
}];

我使用 Instruments Allocations and Leaks 运行我的应用程序,但我没有任何泄漏。

这是我didReceiveMemoryWarning的代码:

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];

    if ([self isViewLoaded] && ([[self view] window] == nil)) {
        self.view = nil;

        [self tearDownGL];

        if ([EAGLContext currentContext] == self.context) {
            [EAGLContext setCurrentContext:nil];
        }
        self.context = nil;
    }

    // Dispose of any resources that can be recreated.
    NSLog(@"Resources freed");
}

tearDownGL 函数释放 OpenGLES 资源,如纹理、顶点数组……

当我运行我的应用程序时,在第一个视图和第二个视图之间多次切换后,我收到内存警告,然后我的应用程序崩溃了。

这是我的问题:

1- 应用程序是否会自动释放我的 UIImage、UIView 等控制器?如果没有,我如何在使用 ARC 时释放它们? 我还看到了 viewDidUnload 函数,但正如文档所述,它已被弃用:

当控制器的视图从内存中释放时调用。 (已弃用 在 iOS 6.0 中。视图不再在内存不足的情况下被清除,并且 所以这个方法永远不会被调用。)

但是,如果在内存不足的情况下不再清除视图,我该如何释放更多内存以防止我的应用程序崩溃? 我该怎么办?

2- 我在我的 2 个控制器的 didReceiveMemoryWarning 函数上设置了一个断点。当我在模拟器上运行应用程序时,我会模拟内存警告。 我可以看到我的 2 个控制器调用了一次 didReceiveMemoryWarning。 但是,如果我在我的第一个和第二个控制器之间切换多次,didReceiveMemoryWarning 会为我的第一个视图控制器调用一次,但我的第二个视图控制器会调用多次。如果我切换 3 次,该函数将被调用 3 次。所以我想,当我“关闭”我的第二个视图以返回第一个视图时,第二个视图没有被释放并且仍然存在。为什么 ?我怎样才能强迫它被摧毁? (因为我不再使用它并创建一个新的) 我在一个函数中创建了第二个视图控制器,我没有保留对它的任何引用(它没有存储在类中)。

【问题讨论】:

    标签: ios objective-c


    【解决方案1】:

    您应该释放(在 ARC 中,这意味着将所有强引用设置为 nil)所有当前不需要并且可以(容易)重新使用的内存(图像、NSData 对象、数组、模型层表示的所有数据等) - 再次使用时创建。如果这些对象在内存警告期间可能已被释放,然后将被重新创建,则所有其他代码都应以检查属性/iVar 是否为零的方式编写。

    我怀疑self.view 是否属于可能被处置的对象之一。

    您可能已经显示了一个 UIImageView。那很可能是用 UIImage 对象创建的。显示 UIImageView 时,您实际上并不需要内存中的 UIImage。 (如果 UIImageView 仍然需要它,那么它会保留它或在其 onw 上保留一个强引用,这样您就不必担心保留图像本身。)这就是要释放的资源。

    如果self.context 属于处置资源,我不能说。很可能是这样。

    【讨论】:

    • 将变量设置为 nil 对我有用。但是很奇怪,为什么不直接在block末尾释放变量呢?
    • 使用 ARC 当没有强引用时它被释放。可能仍然存在弱引用。弱引用应该自动取消,但不要依赖于此。如果没有 ARC,内存可能会在块结束时或多或少地被释放,前提是它被自动释放(并且如果没有其他未自动释放的保留在其上)。
    【解决方案2】:

    ARC 并不总是意味着图像、视图等会立即发布。它被添加到最近的弧池并被释放。如果应用程序可能需要它或在某处使用它,它会被添加到主池中,该主池仅在应用程序终止时才被释放。因此,如果您认为该对象已达到目的,最好自己移除该对象。特别是在图像的情况下,它会保留在内存中,因为它不知道它是否在其他地方被使用。

    【讨论】:

      【解决方案3】:

      无论何时使用块,都应该使用对 self 的弱引用,因为这会导致保留循环。所以把你的代码改成这样:

      __weak typeof(self) blockSelf = self;
      [self dismissViewControllerAnimated:YES completion:^{
          [blockSelf finished];  // Function I use to free some malloc
      }];
      

      另外,释放任何东西的代码应该在 dealloc 中。如果它仅在该控制器的生命周期结束时发生,则您不需要自定义方法。

      你的第一个电话似乎也错了:

      [self presentViewController:secondView animated:YES completion:^{
              [secondView prepareToDraw];   // Function I use to start my computations and rendering
      }];
      

      如果prepareToDraw 只发生一次,当控制器第一次出现时,您应该在viewDidLoad 中运行此代码。这也将有益于您的架构,因为只有控制器本身才应该知道它必须在开始时设置什么并在最后拆除。

      希望对您有所帮助。也许您的代码中还有其他/更多问题。

      【讨论】:

      • 我更改了我的代码。似乎可行,但是,hermann Klecker 是第一个回答的人,他的回答也解决了我的问题,所以我接受了他的回答
      【解决方案4】:

      请查看自动发布池:AutoReleasePools

      上面写着:

      使用本地自动释放池块来减少峰值内存占用

      许多程序会创建自动释放的临时对象。这些对象添加到程序的内存占用,直到块结束。在许多情况下,允许临时对象累积到当前事件循环迭代结束不会导致过多的开销;但是,在某些情况下,您可能会创建大量临时对象,这些对象会大大增加内存占用并且您希望更快地处理这些对象。在后一种情况下,您可以创建自己的自动释放池块。在块的末尾,临时对象被释放,这通常会导致它们的释放,从而减少程序的内存占用

      我有一个类似的问题,我将所有我想摆脱的大对象封装在一个 @autoreleasepool 块中。

      【讨论】:

      • autoreleasepool 似乎工作。你的回答也解决了我的问题。然而 Hermann Klecker 是第一个接受他的回答的人
      猜你喜欢
      • 2011-01-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多