当我创建没有释放 CGImage 的 UIImage 实例时,我似乎遇到了内存管理问题。
我有一个分页 UIScrollView 来滚动浏览一系列 JPG 图像。下面是我的整个课程,它是分页滚动视图中的一个页面视图。
代码正在主线程上运行。该代码使用ARC。我尝试使用 imageWithContentsOfFile: (返回一个自动释放的对象) 以及 initWithContentsOfFile:(返回一个保留的对象) 加载图像。我已经尝试过 @autoreleasepool 并使用 performSelectorOnMainThread 来确保代码正在主线程上运行,如其他帖子中所建议的那样。
当滚动图像时,内存使用量只会增加,直到应用程序终止,如仪器屏幕截图所示。注意图像 io 的高分配。
显示虚拟内存使用情况的屏幕截图
在下面的屏幕截图中,可以看到 GTGImageScrollerPageViews、UIImageViews 和 UIImages 正在被释放。请注意,这些编号在高 300 中存在 Transitory 对象。然而,CGImages 没有被发布,并且 CGImages 的数量处于高 400 和 0 Transitory。
显示分配的屏幕截图
编辑:以前我一直在回收和重新使用 ScrollView 中的 GTGImageScrollerPageView 实例,这是此类滚动视图的常见模式。为了在尝试调试此问题时进行简化,我允许在将整个 GTGImageScrollerPageView 显示在 ScrollView 中后将其释放。正如您在上面的第二张图片中看到的那样,只有 4 个 GTGImageScrollerPageView 活生生的和 377 个暂时性的,还有 388 个 UIImageViews 和 389 个 UIIMages 被列为暂时性,因此看来 UIImageViews 和 UIImages 正在被很好地释放。
如果我使用 CGImageRelease 手动释放 CGImage(在下面的代码中注释掉),则释放 CGImage。我知道我不应该这样做,因为我不拥有 CGImage 但这对于验证这是发生问题的地方很有用。下面的屏幕截图显示了在 Instruments 中测试的相同代码,但 CGImageRelease 未注释。
使用 CGImageRelease 显示虚拟内存使用情况的屏幕截图
显示使用 CGImageRelease 的分配的屏幕截图
在使用 CGImageRelease 的分析输出中,您可以看到正确数量的 CGImage 对象是 Living 和 Transitory,并且内存不会无限增长。此外,应用程序在使用 CGImageRelease 时不会崩溃。
如果这是 CGImage 的一些系统缓存,那么它应该在引发内存警告时释放内存,但事实并非如此。内存只会继续增长。
是什么导致了内存的无限增长?
这是页面视图的代码
编辑:作为对评论的回应,我更新了代码以进一步简化,从而消除了诸如 ivars 和不需要演示问题的属性之类的干扰。问题保持不变,分析时结果相同。我还添加了也输出线程的 NSLog。我确实看到在 GTGImageScrollerPageView 上按预期调用了 dealloc,它始终是线程 #1,并且对 displayAspectThumbImage 的调用始终在线程 #1 上。
我真的不相信这里提供的代码有任何问题,Rob 的慷慨努力证实了这一点。其他原因导致了这种情况,但与加载和显示图像相关的所有代码都在这里;这里没有其他有效的代码。我能想到的唯一另一个值得注意的事情是 displayAspectThumbImage 方法是从 scrollViewDidScroll 和 scrollViewDidEndDecellating 方法调用的,但是在主线程上调用这些方法的事实应该排除由于在另一个线程上运行而导致的自动释放问题.
我已经检查并且没有启用 NSZombies 并且没有僵尸增加我的内存使用量。实际上,当调用 CGImageRelease 方法时,内存使用量非常平稳,如上面的屏幕截图所示。
@implementation GTGImageScrollerPageView
- (void)dealloc {
NSLog(@"%s %@", __PRETTY_FUNCTION__, [NSThread currentThread]);
}
- (void)displayAspectThumbImage:(NSString *)path {
NSLog(@"%s %@", __PRETTY_FUNCTION__, [NSThread currentThread]);
UIImageView *imageView = [[UIImageView alloc] initWithFrame:self.bounds];
[self addSubview:imageView];
UIImage *image = [[UIImage alloc] initWithContentsOfFile:path];
[imageView setImage:image];
//If uncommented CGImages are disposed correctly
//CGImageRelease([imageView.image CGImage]);
}
@end
测试:
iOS 6.1.4
iOS 5.0.1