【问题标题】:How can I resolve my CoreGraphics Memory Leak without crashing?如何在不崩溃的情况下解决我的 CoreGraphics 内存泄漏?
【发布时间】:2012-05-16 15:36:56
【问题描述】:

我有一个代码迭代器,它是一个测试函数,用于将原始数据转换为 UIImage 对象以进行文件存储。我有两个失败路径或选项。我会失败:

A.)通过泄漏(仪器没有显示它 - 仪器所有分配似乎都是稳定的,我必须迭代多次说 50 并且它会通过仪器或正常运行测试功能崩溃) .由于下面的“b”选项,内存似乎是罪魁祸首。

B.)我可以在 UIImage 中强制释放 CG 对象,在这种情况下,应用程序将继续执行。一旦迭代器完成,测试将在最后一次迭代中崩溃,试图释放从我的子函数返回的图像下方的 CGImage。无论我请求多少次迭代,迭代器都会完成它的运行。这表明我已经修复了泄漏。所以现在我想一起想知道最后一次迭代有什么特别之处? (ARC?)

- (UIImage *) convertRGBToUIImage:(unsigned char *) buffer 
                            withWidth:(int) width
                           withHeight:(int) height 
{

   CGColorSpaceRef colorSpace       = nil;
   CGContextRef    context          = nil;
   CGImageRef      ref              = nil;
   UIImage        *retImg           = nil;

   colorSpace = CGColorSpaceCreateDeviceRGB();
   if (colorSpace == NULL)
   {
      NSLog(@"ERROR: allocating color space convert Bitmap\n");
      return nil;
   }

   context = CGBitmapContextCreate (buffer, width, height, 8, width * 4, colorSpace,     kCGImageAlphaPremultipliedLast);
   CGColorSpaceRelease(colorSpace );

   if (context == NULL)
   {
     NSLog(@"ERROR: Context not created!");
     return nil;
   }

   ref = CGBitmapContextCreateImage(context);                                     
   CGContextRelease(context);

   retImg = [UIImage imageWithCGImage:ref];
   CGImageRelease(ref);

   return retImg;
}

迭代器基础

for(int i = 0; i < numberiterations; i++)
{ 
    UIImage *completedImage = [phelper convertRGBToUIImage:pBuffer withWidth:width withHeight:height]

    //...save it out or display it

    //Turn on one of the scenarios below

    //scenario "A" -- will leak memory (instruments doesn't seem to turn anything up)
    //ARC free
    //completedImage = nil; //this will leak and fail after ~50 iterations

    //-or-

    //scenario "B" -- will crash on the very last iteration every time but can run x times
    //CGImageRelease([completedImage CGImage];


}

Xcode 中的分析工具更喜欢上面的场景“A”。再次,我可以做到这一点,一切似乎都很好,但它不会成功完成测试。我认为这指向ARC。我试图弄清楚 __bridge 类型转换没有成功。我似乎无法正确使用语法,也许它不是我问题的答案。任何提示或想法将不胜感激。

【问题讨论】:

    标签: user-interface core-graphics automatic-ref-counting cgimage


    【解决方案1】:

    Instruments 没有显示泄漏的原因是——从技术上讲——没有

    您看到的是来自@autoreleasepool 的内存压力不够紧:

    您的方法 convertRGBToUIImage:withWidth:withHeight: 返回一个新的自动发布的图像,但最近的 @autoreleasepool 不在您的循环中。

    这意味着在循环所在的方法返回之前,所有这些图像都不会被处理掉。

    这就是为什么在场景 A 中您的内存消耗随着每次迭代而增加,而在场景 B 中循环后您的应用程序崩溃的原因:

    UIImage - 主要是 - 一个围绕 CGImageRef 的 Objective-C 包装器,因此您在场景 B 中的附加 CGImageRelease 将后备存储从 UIImage 的脚下拉出。这个后备存储恰好是UIImage 的内存消耗部分,因此您的内存消耗急剧增加几乎消失了。

    @autoreleasepool 在循环返回的方法之后耗尽时,它会处理所有临时的UIImage 实例,而这些临时实例又想要处理它们的支持-CGImageRef …然后boom程序就这样运行了。

    诀窍是在循环内创建自己的@autoreleasepool

    for (int i = 0; i < numberiterations; i++) {
        @autoreleasepool {
            // your old iteration body comes here
        }
    }
    

    现在,场景 A 将按预期运行,而场景 B 在第一次迭代结束时立即崩溃——这也是意料之中的。


    现在是一些严肃的大脑-f**k:

    如果您在 ARC 下以“发布”方案运行您的原始代码(即使用编译器标志 -Os 又名“最快最小”)场景 A 很可能不会崩溃!

    这种看似奇怪的行为的原因是,ARC 用一组特殊的 C 函数来装饰你的代码,这些函数执行实际的内存管理——而且因为这些函数是常量(Objective-C 中的方法调用不一定) 编译器可以优化它们的某些组合。

    这导致UIImage 永远不会被插入到自动释放池中,因此在每次循环迭代后被丢弃。

    【讨论】:

    • 谢谢。无法说出这个问题的调试难度。我的格式不同。我的应用程序(使用 AVFoundation)没有在设备上单独崩溃,而是在仪器下运行时崩溃(没有泄漏,只是内存无限增长直到崩溃)! Instruments 可能在 UIImage 分配和自动释放之间造成了更多的内存压力和延迟。希望有人会受益。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-28
    • 1970-01-01
    • 1970-01-01
    • 2011-05-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多