UIImage正在发布但CGImage尚未发布

时间:2013-05-28 22:38:27

标签: ios memory-management cgimage

我创建UIImage实例时似乎没有内存管理问题,CGImages没有被释放。

我有一个分页UIScrollView来滚动浏览一系列JPG图像。下面是我的整个类,它是分页滚动视图中的页面视图。

代码正在主线程上运行。该代码使用ARC。我尝试使用imageWithContentsOfFile加载图像:(返回一个自动释放的对象)以及initWithContentsOfFile :(返回一个保留的对象)。我已经尝试了@autoreleasepool并使用performSelectorOnMainThread来确保代码在主线程上运行,如其他帖子所示。

当滚动图像时,内存使用量会一直增长,直到应用程序被终止,如图所示。注意图像io的高分配。

显示虚拟内存使用情况的屏幕截图

Virtual Memory Tracker

在下面的屏幕截图中可以看到GTGImageScrollerPageViews,UIImageViews和UIImages正在被释放。请注意,高300中的这些编号有Transitory对象。然而,CGImages没有被发布,CGImages生活的数量在400和0 Transitory。

显示分配的屏幕截图

Allocations

编辑:以前我一直在ScrollView中回收并重新使用GTGImageScrollerPageView实例,就像这样滚动视图的常见模式。为了简化尝试调试此问题的时间,我允许在ScrollView中显示整个GTGImageScrollerPageView后取消分配。正如您在上面的第二张图片中看到的那样,只有4个GTGImageScrollerPageView生活和377个暂时性,还有388个UIImageViews和389个UIIMages被列为暂时的,因此看起来UIImageViews和UIImages正在被解除分配。

如果我用CGImageRelease手动释放CGImage(在下面的代码中注释掉),CGImages将被释放。我知道我不应该这样做,因为我不拥有CGImage,但这有助于验证这是问题发生的地方。下面的屏幕截图显示了在仪器中测试的相同代码,但CGImageRelease未注释。

使用CGImageRelease

显示虚拟内存使用情况的屏幕截图

Virtual memory usage

屏幕截图显示了使用CGImageRelease的分配

enter image description here

在使用CGImageRelease的分析输出中,您可以看到正确数量的CGImage对象是Living和Transitory,并且内存不会无限增长。此外,在使用CGImageRelease时,应用程序不会崩溃。

如果这是CGImage的一些系统缓存,那么它应该在引发内存警告时释放内存,但事实并非如此。记忆力继续增长。

这可能导致内存无限增长?

以下是页面视图的代码

编辑:为了回应评论,我更新了代码以进一步简化,从而消除了诸如ivars和不需要证明问题所需的属性的干扰。问题保持不变,分析时结果相同。我还在NSLog中添加了也输出线程。我确实看到在GTGImageScrollerPageView上按预期调用了dealloc,并且它始终是线程#1,并且对displayAspectThumbImage的调用始终在线程#1上。

我真的不相信这里提供的代码有任何问题,Rob的慷慨努力证实了这一点。还有其他因素造成了这个问题,但所有与加载和显示图像相关的代码都在这里;这里没有其他有效的代码。我能想到的另一个值得注意的事情是,从滚动视图委托的scrollViewDidScroll和scrollViewDidEndDecellerating方法调用displayAspectThumbImage方法,但是在主线程上调用方法的事实应该排除由于在另一个线程上运行而导致的自动释放问题

我已经检查过并且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

3 个答案:

答案 0 :(得分:4)

非常抱歉自己回答这个问题,但认为分享我的经验会很有用。

原来它是UIImage上的一个类别,它是我正在使用的第三方库的一部分。我不会命名库,因为我已经使用最新版本进行测试,问题不存在,因此命名库没有任何好处。

特别奇怪的是,在泄漏的代码附近的任何地方都没有使用该库。它只在整个项目中的一个地方使用,但任何时候我使用UIImage似乎它正在影响UIImage实例。仅仅在项目中存在这一类就足够了。这真是一个惊喜。

我解决这个问题的方法是首先在新项目中模拟场景,然后发现代码没有泄漏。然后,我一次一点地迁移代码,当我移动泄漏的类别时出现。

非常感谢Rob为帮助我做出的慷慨努力,即使他没有直接提供解决方案,与他交谈也非常有助于解决问题。很高兴知道那里有这么酷的人。

答案 1 :(得分:3)

我使用你的代码做了一个简单的无限卷轴,在滚动了近100张图片之后,正如人们所预料的那样,内存使用完全没有问题:

allocations

vm

看看你的来源,我会推荐一些小东西(例如,我会将aspectImageView放入私有类扩展而不是.h,我假设你正在设置pageIndex调用例程,但我将其添加为displayAspectThumbImage的参数,我会将aspectImageView作为weak属性并相应地重构代码(例如创建图像视图,将其添加为子视图,然后将weak imageview属性设置为指向此对象)等),但这些都不会直接影响您的问题。

最重要的是,您的问题不在您与我们分享的代码中。

答案 2 :(得分:0)

尝试使用属性而不是ivar。

@property(非原子,强)UIImageView * aspectImageView;