【问题标题】:Another iPhone - CGBitmapContextCreateImage Leak另一部 iPhone - CGBitmapContextCreateImage 泄漏
【发布时间】:2010-11-28 21:57:29
【问题描述】:

喜欢这篇文章:

我遇到了类似的问题。 create_bitmap_data_provider 中 malloc 的指针永远不会被释放。我已经验证了关联的图像对象最终被释放,而不是提供者的分配。我应该明确创建一个数据提供者并以某种方式管理它的内存吗?看起来像个黑客。

CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(NULL, blah blah blah);
CGColorSpaceRelease(colorSpace);

// ... draw into context

CGImageRef imageRef = CGBitmapContextCreateImage(context);
UIImage * image = [[UIImage alloc] initWithCGImage:imageRef];

CGImageRelease(imageRef);
CGContextRelease(context);

在fbrereto下面的回答之后,我将代码更改为:

- (UIImage *)modifiedImage {
    CGSize size = CGSizeMake(width, height);

    UIGraphicsBeginImageContext(size);
    CGContextRef context = UIGraphicsGetCurrentContext();

    // draw into context   

    UIImage * image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return image;  // image retainCount = 1
}    

// caller: 
{
    UIImage * image = [self modifiedImage]; 
    _imageView.image = image; // image retainCount = 2
}

// after caller done, image retainCount = 1, autoreleased object lost its scope

不幸的是,这仍然会出现相同的问题,即水平翻转图像的副作用。它似乎在内部对 CGBitmapContextCreateImage 做同样的事情。

我已验证我的对象的 dealloc 已被调用。在我释放_imageView 之前,_imageView.image_imageView 上的retainCount 都是1。这真的没有意义。其他人似乎也有这个问题,我是最后一个怀疑SDK的人,但是这里会不会有iPhone SDK的错误???

【问题讨论】:

  • 不要使用 UIGraphicsBeginImageContext:在多线程应用程序中不安全。
  • 知道了,我还是把它拿出来做水平图像翻转。

标签: iphone uiimage memory-leaks


【解决方案1】:

看起来问题在于您正在释放指向返回的CGImage 的指针,而不是CGImage 本身。我之前也遇到过类似的问题,分配不断增加,最终导致应用程序崩溃。我通过分配CGImage 而不是CGImageRef 来解决这个问题。更改后在 Insturments 中使用分配运行您的代码,您应该不会再看到 malloc 的永久内存消耗。同样,如果您使用类方法imageWithCGImage,您将不必担心稍后自动释放您的UIImage

我是在 PC 上输入的,所以如果你把它直接放到 XCode 中,你可能会遇到语法问题,我提前道歉;但是校长是正确的。

CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 
CGContextRef context = CGBitmapContextCreate(NULL, blah blah blah); 
CGColorSpaceRelease(colorSpace);

// ... draw into context  

CGImage cgImage = CGBitmapContextCreateImage(context); 
UIImage * image = [UIImage imageWithCGImage:cgImage];  
CGImageRelease(cgImage); 
CGContextRelease(context);
return image;

【讨论】:

    【解决方案2】:

    我遇到了这个问题,这让我发疯了好几天。在使用 Instruments 进行大量挖掘并注意到 CG Raster Data 中的泄漏之后。

    问题似乎出在 CoreGraphics 内部。我的问题是当我在一个紧密的循环中使用 CGBitmapContextCreateImage 时,它​​会在一段时间内保留一些图像(每个 800kb),这会慢慢泄露出去。

    在使用 Instruments 跟踪几天后,我发现一种解决方法是改用 CGDataProviderCreateWithData 方法。有趣的是,输出是相同的 CGImageRef,但这次 Core 图形在 VM 中没有使用 CG Raster Data,也没有泄漏。我假设这是一个内部问题或滥用它。

    这是拯救我的代码:

    @autoreleasepool {
        CGImageRef cgImage;
        CreateCGImageFromCVPixelBuffer(pixelBuffer,&cgImage);
    
        UIImage *image= [UIImage imageWithCGImage:cgImage scale:1.0 orientation:UIImageOrientationUp];
    
        // DO SOMETHING HERE WITH IMAGE
    
        CGImageRelease(cgImage);
    }
    

    关键是在下面的方法中使用了 CGDataProviderCreateWithData。

    static OSStatus CreateCGImageFromCVPixelBuffer(CVPixelBufferRef pixelBuffer, CGImageRef *imageOut)
        {
            OSStatus err = noErr;
            OSType sourcePixelFormat;
            size_t width, height, sourceRowBytes;
            void *sourceBaseAddr = NULL;
            CGBitmapInfo bitmapInfo;
            CGColorSpaceRef colorspace = NULL;
            CGDataProviderRef provider = NULL;
            CGImageRef image = NULL;
    
            sourcePixelFormat = CVPixelBufferGetPixelFormatType( pixelBuffer );
            if ( kCVPixelFormatType_32ARGB == sourcePixelFormat )
                bitmapInfo = kCGBitmapByteOrder32Big | kCGImageAlphaNoneSkipFirst;
            else if ( kCVPixelFormatType_32BGRA == sourcePixelFormat )
                bitmapInfo = kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst;
            else
                return -95014; // only uncompressed pixel formats
    
            sourceRowBytes = CVPixelBufferGetBytesPerRow( pixelBuffer );
            width = CVPixelBufferGetWidth( pixelBuffer );
            height = CVPixelBufferGetHeight( pixelBuffer );
    
            CVPixelBufferLockBaseAddress( pixelBuffer, 0 );
            sourceBaseAddr = CVPixelBufferGetBaseAddress( pixelBuffer );
    
            colorspace = CGColorSpaceCreateDeviceRGB();
    
            CVPixelBufferRetain( pixelBuffer );
            provider = CGDataProviderCreateWithData( (void *)pixelBuffer, sourceBaseAddr, sourceRowBytes * height, ReleaseCVPixelBuffer);
            image = CGImageCreate(width, height, 8, 32, sourceRowBytes, colorspace, bitmapInfo, provider, NULL, true, kCGRenderingIntentDefault);
    
            if ( err && image ) {
                CGImageRelease( image );
                image = NULL;
            }
            if ( provider ) CGDataProviderRelease( provider );
            if ( colorspace ) CGColorSpaceRelease( colorspace );
            *imageOut = image;
            return err;
        }
    
        static void ReleaseCVPixelBuffer(void *pixel, const void *data, size_t size)
        {
            CVPixelBufferRef pixelBuffer = (CVPixelBufferRef)pixel;
            CVPixelBufferUnlockBaseAddress( pixelBuffer, 0 );
            CVPixelBufferRelease( pixelBuffer );
        }
    

    【讨论】:

      【解决方案3】:

      建议您使用UIGraphicsBeginImageContext,而不是手动创建您的CGContextRef,如this post 中所示。有关该组例程的更多详细信息,请参见 here。我相信它会帮助解决这个问题,或者至少给你更少的内存,你必须自己管理。

      更新:

      给定新代码,函数中的UIImageretainCount 将为1,将其分配给imageView 的图像将导致它碰撞到2。此时释放imageView 将将UIImageretainCount 保留为1,导致泄漏。那么重要的是,在将UIImage 分配给imageView 之后,再分配给release 它。它可能看起来有点奇怪,但它会导致 retainCount 正确设置为 1。

      【讨论】:

      • 添加到此答案之后的问题中。没有帮助。
      • 在您添加到您的问题后添加到我的答案中。希望它会有所帮助。
      • 它必须是我的代码,我不能责怪SDK(3.0)。但是函数中的 retainCount 将是 autoreleased 1。一旦调用者的范围消失,它就会减 1。所以在 imageView 上设置之后,它将是 2,但是一旦调用者的范围完成,它就会变成一个。在 dealloc 中,我在对象的发布中验证了 imageView 和 imageView.image 的 retainCount 均为 1。
      • 如果对象是自动释放的,那么您必须确保清空池以释放图像。你在这样做吗?
      • 应用程序最终会因此耗尽内存。我不应该为所有自动释放的对象耗尽池。它最终应该像我拥有的​​所有其他自动释放对象一样释放自己。这只小野兽因为某种原因不想放弃自己。如果您想立即检查内存,我可以理解是否会耗尽它的想法,但是我一遍又一遍地运行此代码,内存不断增长。
      【解决方案4】:

      你不是唯一一个遇到这个问题的人。我在使用 CGBitmapContextCreateImage() 时遇到了重大问题。当你打开僵尸模式时,它甚至会警告你内存被释放了两次(如果不是这样的话)。将 CG* 内容与 UI* 内容混合时肯定会出现问题。我仍在试图弄清楚如何围绕这个问题编写代码。

      旁注:调用 UIGraphicsBeginImageContext 不是线程安全的。小心点。

      【讨论】:

      • 好吧,如果你弄明白了,请发表答案! :)
      • 是的,UIGraphicsBeginImageContext 不是线程安全的,但这还不是全部。
      【解决方案5】:

      这对我很有帮助!以下是我如何使用它来解决令人讨厌的泄漏问题:

          CGImage *cgImage = CGBitmapContextCreateImage(context);
          CFDataRef dataRef = CGDataProviderCopyData(CGImageGetDataProvider(cgImage));
          CGImageRelease(cgImage);
          image->imageRef = dataRef;
          image->image = CFDataGetBytePtr(dataRef);
      

      注意,我必须将 CFDataRef(对于 CFRelease(image->imageRef))存储在 ~Image 函数中。希望这也对其他人有所帮助...JR

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-11-12
        • 1970-01-01
        • 1970-01-01
        • 2011-04-13
        • 1970-01-01
        • 1970-01-01
        • 2011-06-06
        相关资源
        最近更新 更多