另一个iPhone - CGBitmapContextCreateImage泄漏

时间:2009-09-16 18:33:00

标签: iphone uiimage memory-leaks

喜欢这篇文章:

我遇到了类似的问题。永远不会释放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.image之前,_imageView_imageView上的retainCount都是1。这真的没有意义。其他人似乎也有这个问题,我是最后一个怀疑SDK的人,但这里可能有iPhone SDK错误???

5 个答案:

答案 0 :(得分:4)

看起来问题是你发布了一个指向返回的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;

答案 1 :(得分:3)

我遇到了这个问题,它让我疯了几天。经过多次挖掘并注意到使用仪器的CG栅格数据泄漏。

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

在使用Instruments进行几天跟踪后,我发现一种解决方法是使用CGDataProviderCreateWithData方法。有趣的是输出是相同的CGImageRef,但这次没有在Core by Core图形中使用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 );
    }

答案 2 :(得分:0)

而不是手动创建CGContextRef,我建议您使用this post中演示的UIGraphicsBeginImageContext。可以找到关于该组例程的更多细节here。我相信它有助于解决这个问题,或者至少可以减少你自己管理的记忆。

更新:

鉴于新代码,retainCount的{​​{1}}因为它来自函数将为1,并将其分配给imageView的图像将导致它碰到2.此时取消分配imageView会使UIImage的{​​{1}}为1,从而导致泄漏。然后,在将retainCount分配给imageView后,将UIImage传递给UIImage非常重要。它可能看起来有点奇怪,但它会使release正确设置为1。

答案 3 :(得分:0)

你不是唯一有这个问题的人。我在CGBitmapContextCreateImage()方面遇到了重大问题。当您打开 Zombie 模式时,它甚至会警告您内存被释放两次(当情况不是这样时)。将CG *东西与UI *东西混合时肯定存在问题。我还在试图弄清楚如何解决这个问题。

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

答案 4 :(得分:0)

这对我有帮助!以下是我用它来解决这个令人讨厌的泄漏问题的方法:

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

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