0

我有一个 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 时,我的详细视图控制器实例被释放。

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

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

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

4

1 回答 1

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 框架NSCache在适当的地方使用 s,如果没有人可以将内存分配给进程,那么这两个操作系统都不会费心从进程中取出内存,因为这只是一种毫无意义的处理开销。您应该使用 Instruments,尤其是因为它可以按类型和计数分解内存中的内容,让您不仅知道总计数,还知道您将内存用于什么。

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

于 2013-06-09T19:13:40.383 回答