内存警告和崩溃(ARC) - 如何识别它发生的原因?

xiu*_*ius 15 core-graphics objective-c ios automatic-ref-counting

我最近开始使用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;
}
Run Code Online (Sandbox Code Playgroud)

有没有其他方法不会吃掉这么多内存,或者在使用ARC时代码出了什么问题呢?

发生内存警告+崩溃的另一个地方是任何视图的重绘过多.它不需要很快,只需要很多次.内存堆栈直到它崩溃,我无法找到任何真正的责任.(我可以在VM Tracker中看到不断增长的驻留/脏内存以及Allocations工具中的堆增长)

我的问题主要是:如何找到它为什么会发生?我的理解是,当没有给定对象的所有者时,它会尽快发布.我对代码的检查表明,即使我没有看到任何理由,也没有发布任何对象.我不知道任何保留周期......

我已经阅读了过渡到ARC发行说明,bbum关于堆分析的文章,可能还有十几个.有和没有ARC的堆分析不一样?我似乎无法对其输出做任何有用的事情.

谢谢你的任何想法.

更新:(不要强迫所有人阅读所有评论并履行我的承诺)

通过仔细检查我的代码并添加@autoreleasepool,它有任何意义,内存消耗降低了.最大的问题是UIGraphicsBeginImageContext从后台线程调用.在修复它之后(请参阅@Tammo Freese的答案以获取详细信息),释放很快就会导致应用程序崩溃.

我的第二次崩溃(由许多重绘同一图表引起)CGContextFlush(context)在我的绘图方法结束时添加完全解决了 .对我感到羞耻.


对于试图做类似事情的人来说,这是一个小警告:使用OpenGL.CoreGraphics不足以快速制作大型绘图,特别是在iPad 3上没有.(第一个带有视网膜)

Tam*_*ese 18

回答你的问题:使用ARC识别内存警告和崩溃的问题基本上像以前一样使用手动保留释放(MRR).ARC使用retain,release并且autorelease就像MRR,它只是插入您的通话,并具有一定的优化到位,即使是应该降低在某些情况下,内存消耗.

关于你的问题:

您发布的仪器屏幕截图中,可以看到分配峰值.在我到目前为止遇到的大多数情况下,这些尖峰是由自动释放的物体悬挂太久造成的.

  1. 你提到过你使用过NSOperationQueue.如果覆盖-[NSOperationQueue main],请确保包含方法的全部内容@autoreleasepool { ... }.自动释放池可能已经到位,但不能保证(即使有一个,也可能比你想象的要长一些).

  2. 如果1.没有帮助,并且您有一个处理图像的循环,请将循环的内部部分包裹起来,@autoreleasepool { ... }以便立即清除临时对象.

  3. 你提到过你使用过NSOperationQueue.从iOS 4开始,在UIKit中绘制到图形上下文是线程安全的,但如果文档是正确的,UIGraphicsBeginImageContext那么仍然只能在主线程上调用!更新:文档现在声明,自从iOS 4以来,可以从任何线程调用该函数,以下实际上是不必要的!为了安全起见,使用创建上下文CGBitmapContextCreate并检索图像CGBitmapContextCreateImage.这些方面的东西:

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef context = CGBitmapContextCreate(NULL, width, height, 8, 0, colorSpace, kCGImageAlphaPremultipliedLast);
    CGColorSpaceRelease(colorSpace);
    
    // draw to the context here
    
    CGImageRef newCGImage = CGBitmapContextCreateImage(context);
    CGContextRelease(context);
    UIImage *result = [UIImage imageWithCGImage:newCGImage scale:scale orientation: UIImageOrientationUp];
    CGImageRelease(newCGImage);
    
    return result;
    
    Run Code Online (Sandbox Code Playgroud)