1

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

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

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

- (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 类型转换但没有成功。我似乎无法正确使用语法,也许它不是我问题的答案。任何提示或想法将不胜感激。

4

1 回答 1

3

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

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

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

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

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

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

当在返回循环的方法之后耗尽了,它会处理所有临时实例,而这些临时@autoreleasepool实例反过来UIImage又想要处理它们的支持CGImageRef——……然后程序就开始了。

@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永远不会被插入到自动释放池中,因此,在循环的每次迭代之后都会被丢弃。

于 2012-05-19T14:00:25.063 回答