iOS 内存使用量增加直到崩溃

Posted

技术标签:

【中文标题】iOS 内存使用量增加直到崩溃【英文标题】:iOS memory usage increases until crash 【发布时间】:2013-06-09 19:02:41 【问题描述】:

我有一个 ios 应用程序,它使用 UINavigationController 以相当简单的主/细节方式向前(推送)和向后(弹出)移动。

详细视图控制器有 2 或 3 个全屏图像,我使用手动添加

layer1 = [[UIImageView alloc] initWithFrame:layer1Rect];//layer1rect is a CGRect
UIImage *img1 = [[UIImage alloc]initWithContentsOfFile:imgName];//imgName is an image in the bundle
layer1.image = img1;
layer1.alpha = 1;
[self.view addSubview:layer1];
img1 = nil;

在我看来DidUnload 我有

for (UIView *subview in [self.view subviews]) 
        [subview removeFromSuperview];


layer1 = nil;
layer2 = nil;
layer3 = nil;

我遇到的问题是,在推送和弹出十几次之后,我收到了内存警告和崩溃。

我正在使用 ARC,尽管从我在其他地方读到的内容本身不太可能是问题的根源。

当我在仪器中(在设备上,而不是模拟器上)运行应用程序时,我看到了一些有趣的结果。

首先,内存分配工具显示不到 2MB 的内存使用量并且没有泄漏,直到出现警告和崩溃。我还可以看到,当我执行 pop 时,我的详细视图控制器实例被释放。

但是,如果我查看 Activity Monitor,我发现使用量从大约 50MB 开始,并且每次我推送到详细视图控制器时都会继续增加,直到它在大约 250MB 时崩溃。

我认为可能图像正在被缓存,但我没有在任何地方使用 imageName:,这是缓存的常见原因。

那么我错过了什么?如何确保当我将详细视图控制器从堆栈中弹出时,它正在使用的所有内存都再次可用?

【问题讨论】:

【参考方案1】:

viewDidUnload 如果您的控制器的视图由于内存不足警告而被逐出内存,则调用。删除视图并稍后再次加载是UIViewControllers 在 iOS 2-5 下处理低内存警告的方式。

在 iOS 6 下,视图控制器永远不会丢弃它的视图。所以你永远不会得到viewDidUnload。在您的情况下,这意味着您每次运行第一个代码块时都会添加另一个UIImage(我假设它不在viewDidLoad 中?)。旧的不会被释放,因为它有一个超级视图;您发布对它的引用没有任何区别。

此外,initWithContentsOfFile: 最好表示为 [UIImage imageNamed:],因为后者显式缓存图像,而前者每次都从磁盘重新加载,从而创建像素内容的新副本。

所以建议的改变是改变这个:

layer1 = [[UIImageView alloc] initWithFrame:layer1Rect];

到这里:

[layer1 removeFromSuperview];
layer1 = [[UIImageView alloc] initWithFrame:layer1Rect];

如果您的视图层次结构中有一个现有的layer1,当您在initWithFrame: 行上隐式释放对它的引用时,这将确保它被释放。

还有:

UIImage *img1 = [[UIImage alloc]initWithContentsOfFile:imgName];

最好是:

UIImage *img1 = [UIImage imageNamed:imgName];

因此,在整个应用中多次使用具有该文件名的图像肯定都指向同一个实例,但如果警告需要,它仍会从内存中删除。

至于诊断,请尝试打开 NSZombies。 NSZombies 使已释放的对象保持活动状态,通常使用它来捕获悬空指针的使用。在这种情况下,您可以做的是打开僵尸并查看它是否会显着改变您的内存占用。如果不是,则说明您实际上并没有释放事物。

Activity Monitor 不一定是一种可靠的措施——iOS 框架在适当的情况下使用NSCaches,如果没有人给它,任何操作系统都不会费心从进程中取出内存,因为这只是处理的无意义支出.您应该使用 Instruments,尤其是因为它可以按类型和计数分解内存中的内容,让您不仅知道总计数,还知道您将内存用于什么。

这与解释为什么imageNamed: 通常是更好的选择,尽管您的 cmets 是相同的逻辑。只要有多余的内存来存放它们,您之前加载的图像就会一直保留在内存中。如果收到内存警告并且没有人使用它们,它们就不会留下来。仅当您对内存警告的响应非常昂贵时才需要关注它,并且通常它通过确保重用资源具有相同的身份而不是副本来帮助避免内存警告。

【讨论】:

感谢您的建议。我会试试你的建议。 我想我对viewDidUnload操作的理解在iOS6中是不正确的。打开 NSZombie 是个好主意。它向我展示了我的细节视图控制器有多个僵尸实例,我认为它们都被释放了。我不明白为什么会这样?保留/释放周期由 ARC 控制。我该怎么做才会导致问题?

以上是关于iOS 内存使用量增加直到崩溃的主要内容,如果未能解决你的问题,请参考以下文章

如何找出哪个课程占用的内存最多?

Elasticsearch 段建立,堆增加直到崩溃

内存增加每次核心数据迭代

内存使用量增长,直到VM在使用Java 8运行Wildfly 9时崩溃

应用程序占用太多内存直到崩溃! (苹果手机)

iOS 内存增加问题