【发布时间】:2012-07-19 16:56:11
【问题描述】:
我最近开始使用 ARC,从那时起我就把每一个内存问题都归咎于它。 :) 也许,你可以帮助我更好地理解我做错了什么。
我目前的项目主要是关于 CoreGraphics - 图表绘制、带有缩略图的视图等等。我相信使用手动内存管理不会有问题,除了一些僵尸......但到目前为止,每次我尝试创建大量缩略图或重绘更复杂的图表时,应用程序都会崩溃。
在使用 Instruments 进行分析时,我可以在常驻内存和脏内存中看到非常高的价值。堆分析显示相当惊人的不规则增长...
仅绘制几个缩略图时,常驻内存会增长约 200 MB。绘制完所有内容后,内存会回落到与绘制前几乎相同的值。但是,对于很多缩略图,常驻内存中的值高于400 MB,这显然会使应用程序崩溃。我试图限制同时绘制的缩略图数量(NSOperationQueue 及其 maxConcurrentOperationCount),但由于释放这么多内存似乎需要更多时间,它并没有真正解决问题。
现在我的应用程序基本上无法正常工作,因为真实数据可以处理大量复杂的图表 = 大量缩略图。
每个缩略图都是用我从这里得到的代码创建的:(UIImage 上的类别)
+ (void)beginImageContextWithSize:(CGSize)size
{
if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) {
if ([[UIScreen mainScreen] scale] == 2.0) {
UIGraphicsBeginImageContextWithOptions(size, YES, 2.0);
} else {
UIGraphicsBeginImageContext(size);
}
} else {
UIGraphicsBeginImageContext(size);
}
}
+ (void)endImageContext
{
UIGraphicsEndImageContext();
}
+ (UIImage*)imageFromView:(UIView*)view
{
[self beginImageContextWithSize:[view bounds].size];
BOOL hidden = [view isHidden];
[view setHidden:NO];
[[view layer] renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
[self endImageContext];
[view setHidden:hidden];
return image;
}
+ (UIImage*)imageFromView:(UIView*)view scaledToSize:(CGSize)newSize
{
UIImage *image = [self imageFromView:view];
if ([view bounds].size.width != newSize.width ||
[view bounds].size.height != newSize.height) {
image = [self imageWithImage:image scaledToSize:newSize];
}
return image;
}
+ (UIImage*)imageWithImage:(UIImage*)image scaledToSize:(CGSize)newSize
{
[self beginImageContextWithSize:newSize];
[image drawInRect:CGRectMake(0,0,newSize.width,newSize.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
[self endImageContext];
return newImage;
}
在使用 ARC 时,是否有其他方法不会占用太多内存或代码确实有问题?
发生内存警告+崩溃的另一个地方是任何视图的重绘过多。它不需要很快,只要多次。内存堆积起来,直到它崩溃,我无法找到任何真正对它负责的东西。 (我可以在 VM Tracker 中看到不断增长的常驻/脏内存和在 Allocations 工具中的堆增长)
我的问题基本上是:如何找到它为什么会发生?我的理解是当给定对象没有所有者时,它会尽快发布。我对代码的检查表明,很多对象根本没有被释放,即使我没有看到任何发生的原因。我不知道任何保留周期...
我已经阅读了过渡到 ARC 发行说明、bbum 的关于堆分析的文章以及可能还有其他十几个文章。使用和不使用 ARC 的堆分析有何不同?我似乎无法用它的output 做任何有用的事情。
感谢您的任何想法。
更新:(不强迫每个人阅读所有的 cmets 并遵守我的承诺)
通过仔细检查我的代码并添加@autoreleasepool,它有任何意义,内存消耗降低了。最大的问题是从后台线程调用UIGraphicsBeginImageContext。修复后(详见@Tammo Freese 的回答)释放很快就不会导致应用崩溃。
我的第二次崩溃(由多次重绘同一图表引起)通过在我的绘图方法末尾添加CGContextFlush(context) 完全解决。真丢人。
对任何尝试做类似事情的人的一个小警告:使用 OpenGL。CoreGraphics 对于大型绘图的动画效果不够快,尤其是在 iPad 3 上。(第一个使用视网膜)
【问题讨论】:
-
编译器不会自动管理 Core Foundation 对象的生命周期;您必须按照 Core Foundation 内存管理规则的规定调用 CFRetain 和 CFRelease(或相应的特定于类型的变体)(请参阅 Core Foundation 的内存管理编程指南)。请参阅developer.apple.com/library/ios/#releasenotes/ObjectiveC/… 也可以在此处找到有关 ARC、免费桥接和核心图形的优秀帖子:stackoverflow.com/a/7800359/312312
-
@Lefteris 谢谢,我已经阅读了 Apple 的文章并观看了您链接的答案中提到的视频,但是当我画画时,我并没有真正保留任何东西,只是简单的
CGContextAddLineToPoint,CGContextAddRect,... 返回 void。我应该释放从CGContextRef context = UIGraphicsGetCurrentContext();获得的上下文吗?不过,我会调查一下。
标签: objective-c ios core-graphics automatic-ref-counting