-[UIImage drawInRect:] / CGContextDrawImage() 不释放内存?

Posted

技术标签:

【中文标题】-[UIImage drawInRect:] / CGContextDrawImage() 不释放内存?【英文标题】:-[UIImage drawInRect:] / CGContextDrawImage() not releasing memory? 【发布时间】:2010-06-02 21:35:15 【问题描述】:

我想轻松地将 UIImage 混合到另一个背景图像之上,所以为 UIImage 编写了一个类别方法,改编自 blend two uiimages based on alpha/transparency of top image:

- (UIImage *) blendedImageOn:(UIImage *) backgroundImage  
 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
 UIGraphicsBeginImageContext(backgroundImage.size);

 CGRect rect = CGRectMake(0, 0, backgroundImage.size.width, backgroundImage.size.height);
 [backgroundImage drawInRect:rect];
 [self drawInRect:rect];
 UIImage* blendedImage = [UIGraphicsGetImageFromCurrentImageContext() retain];

 UIGraphicsEndImageContext();
 [pool release];

 return [blendedImage autorelease];

不幸的是,我的应用程序使用上述方法加载大约 20 张图像并将它们与背景和光泽图像混合(因此可能大约 40 次调用),正在设备上被抛弃。

Instruments 会话显示,源自对 drawInRect: 的调用对 malloc 的调用是造成大部分内存使用的原因。我尝试用对函数 CGContextDrawImage 的等效函数调用替换 drawInRect: 消息,但它没有帮助。 AutoReleasePool是我发现内存使用问题后添加的;它也没有什么不同。

我想这可能是因为我没有正确使用图形上下文。由于我创建的上下文数量,在循环中调用上述方法是一个坏主意吗?还是我只是错过了什么?

- 编辑1:感谢cmets。该方法在设置 20 个视图的控制器方法中调用,因此在循环中我有以下调用:

    UIImage *blendedImage = [newImage blendedImageOn:backgroundImage];

我添加了自动释放池以确保图像在主自动释放池释放它们之前被释放,所以理论上所有新的 UIImages 对象都应该在循环完成时释放。无论自动释放池是否在其中,分析结果都没有显示任何差异。

- 编辑 2:是的,我尝试在调用 blendedImageOn: 之前添加一个自动释放池,但没有效果。

- 编辑 3:修复了 UIImage 由于自动释放池而被释放的尴尬错误。自动释放池的目的是释放结果 UIImage 之外的任何对象,以防由于添加到主自动释放池中的临时对象未立即释放而导致内存过多。

我试图问的问题(非常糟糕,我承认!)是:为什么调用此方法 20 次会导致大量内存使用?

【问题讨论】:

【参考方案1】:

你不应该直接调用drawRect: 方法。改用[self setNeddsDisplay];(它不会帮助你解决这个泄漏);

关于您的泄漏。删除有关池的所有内容。 您的方法返回自动释放的 UIImage 对象。 请将代码粘贴到您使用返回的UIImage 的位置,我会为您提供帮助。 您很可能应该在调用 blendedImageOn: 的地方创建池,并在每 3-5 次迭代中耗尽池。

【讨论】:

它是drawInRect,将一个图像写入另一个图像。很确定 setNeedsDisplay 不会那样做! 对不起,我的错误,请阅读 drawRect: 而不是 drawInRect。无论如何,答案的第二部分仍然存在。最好展示作者如何调用 blendedImageOn 方法。 是的。 +1 用于捕获池中的自动释放对象。 (这听起来完全不对。)【参考方案2】:

我发现内存使用过多的问题。我的图像比我预期的要大得多,并且在调用将它们绘制到图形上下文中的发布方法之前将背景图像调整为视图大小,解决了内存使用问题。

我仍然很困惑为什么图像上下文结束时没有回收内存。

【讨论】:

【参考方案3】:

您的代码的一个问题是您获得的 blendedImage 对象被标记为自动释放,当您释放临时自动释放池时,噗,您也释放了 blendedImage。

您可以保留直到池被释放,然后自动释放:

...
UIImage* blendedImage = UIGraphicsGetImageFromCurrentImageContext(); 

UIGraphicsEndImageContext();
[blendedImage retain];       // keep this past pool release
[pool release];

[blendedimage autorelease];  // now it will be autoreleased from the main pool

return [blendedImage release];  // release to balance out the retain above

【讨论】:

程序,好收获。事实上,我已经发布了故障排除代码,它没有表现出我的症状,但正如你所指出的那样,它表现出完全不同的内存问题。所有,抱歉造成混乱 :-( 我已经编辑了代码并重新表述了问题描述,希望它能澄清我遇到的问题。【参考方案4】:

根据iPhone memory usage guidelines,任何返回“拥有”对象(非自动释放)的方法都应该以“alloc”或“new”开头,或者它应该包含“copy”。

您的名字“blendedImageOn”不符合这些准则。如果你用了一个月,或者别人用了,你就不会记得里面保留了图片,就会出现内存泄漏。

【讨论】:

名字好点,我猜名字应该是更详细的'imageByBlendingImage:'。但是,该方法没有返回“拥有”对象,因为由于自动释放,在返回时保留计数应平衡为 0。这符合内存使用指南:您通过向对象发送释放消息或自动释放消息来放弃对对象的所有权(自动释放在“自动释放”中有更详细的讨论)。因此,在 Cocoa 术语中,放弃对象的所有权通常称为“释放”对象。 对此感到抱歉.. 看来我是在评论旧版本的代码,或者我没有看清楚 ;-) 当然现在看起来代码返回了一个自动释放的对象,并且方法名没问题。

以上是关于-[UIImage drawInRect:] / CGContextDrawImage() 不释放内存?的主要内容,如果未能解决你的问题,请参考以下文章

drawInRect: withAttributes 文本垂直居中或放置一些填充

drawInRect:失去图像分辨率的质量

– drawInRect:withAttributes: 设置 UIFont 时崩溃

具有相同图像的慢速drawInRect

在drawInRect中绘制渐变层

drawAtPoint: 和 drawInRect: 模糊文本