另一款 iPhone - CGBitmapContextCreateImage 泄漏

发布于 2024-08-05 03:19:21 字数 1624 浏览 1 评论 0原文

就像这篇文章一样:

我有一个类似的问题。 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 错误???

Like in this post:

I'm having a similar problem. The pointer from the malloc in create_bitmap_data_provider is never freed. I've verified that the associated image object is eventually released, just not the provider's allocation. Should I explicitly create a data provider and somehow manage it's memory? Seems like a hack.

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);

After fbrereto's answer below, I changed the code to this:

- (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

Unfortunately, this still exhibits the same issue with a side effect of flipping the image horizontally. It appears to do the same thing with CGBitmapContextCreateImage internally.

I have verified my object's dealloc is called. The retainCount on the _imageView.image and the _imageView are both 1 before I release the _imageView. This really doesn't make sense. Others seem to have this issue as well, I'm the last one to suspect the SDK, but could there be an iPhone SDK bug here???

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

肥爪爪 2024-08-12 03:19:21

看起来问题在于您正在释放指向返回的 CGImage 的指针,而不是 CGImage 本身。我之前也遇到过类似的问题,分配量不断增加,最终应用程序崩溃。我通过分配 CGImage 而不是 CGImageRef 来解决这个问题。更改后,在带有分配的 Instruments 中运行代码,您应该不会再看到 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;

It looks like the problem is that you are releasing a pointer to the returned CGImage, rather than the CGImage itself. I too was having similar issues before with continual growing allocations and an eventual app crash. I addressed it by allocating a CGImage rather than a CGImageRef. After the changes run your code in Insturments with allocations, and you should not see anymore perpetual memory consumption from malloc. As well if you use the class method imageWithCGImage you will not have to worry about autoreleasing your UIImage later on.

I typed this on a PC so if you drop it right into XCode you may have syntax issue, I appologize in advance; however the principal is sound.

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;
﹎☆浅夏丿初晴 2024-08-12 03:19:21

我遇到了这个问题,这让我抓狂了几天。经过大量挖掘并注意到使用 Instruments 的 CG 栅格数据泄漏。

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

经过几天的 Instruments 跟踪后,我发现一个解决方法是使用 CGDataProviderCreateWithData 方法。有趣的是,输出是相同的 CGImageRef,但这次 Core Graphics 在 VM 中不会使用 CG 光栅数据,也不会泄漏。我假设这是一个内部问题或滥用它。

这是拯救我的代码:

@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 );
    }

I had this problem and it drove me nuts for a few days. After much digging and noticing a leak in CG Raster Data using Instruments.

The problem seems to lie inside CoreGraphics. My problem was when i was using CGBitmapContextCreateImage in a tight loop it would over a period of time retain some images (800kb each) and this slowly leaked out.

After a few days of tracing with Instruments I found a workaround was to use CGDataProviderCreateWithData method instead. The interesting thing was the output was the same CGImageRef but this time there would be no CG Raster Data used in VM by Core graphics and no leak. Im assuming this is an internal problem or were misusing it.

Here is the code that saved me:

@autoreleasepool {
    CGImageRef cgImage;
    CreateCGImageFromCVPixelBuffer(pixelBuffer,&cgImage);

    UIImage *image= [UIImage imageWithCGImage:cgImage scale:1.0 orientation:UIImageOrientationUp];

    // DO SOMETHING HERE WITH IMAGE

    CGImageRelease(cgImage);
}

The key was using CGDataProviderCreateWithData in the method below.

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 );
    }
惟欲睡 2024-08-12 03:19:21

我建议您不要手动创建 CGContextRef,而是利用 UIGraphicsBeginImageContext,如 这篇文章。有关这组例程的更多详细信息,请参阅 在这里。我相信这将有助于解决这个问题,或者至少让您减少管理自己的记忆。

更新:

根据新代码,从函数中出来的 UIImageretainCount 将为 1,将其分配给 imageView 的图像将导致它碰撞到2. 此时,释放 imageView 将使 UIImageretainCount 为 1,从而导致泄漏。因此,在将 UIImage 分配给 imageView 后,release 它很重要。它可能看起来有点奇怪,但它会导致 retainCount 被正确设置为 1。

Instead of manually creating your CGContextRef I'd suggest you leverage UIGraphicsBeginImageContext as demonstrated in this post. More details on that set of routines can be found here. I trust it'll help to resolve this issue, or at the very least give you less memory you have to manage yourself.

UPDATE:

Given the new code, the retainCount of the UIImage as it comes out of the function will be 1, and assigning it to the imageView's image will cause it to bump to 2. At that point deallocating the imageView will leave the retainCount of the UIImage to be 1, resulting in a leak. It is important, then, after assigning the UIImage to the imageView, to release it. It may look a bit strange, but it will cause the retainCount to be properly set to 1.

ま柒月 2024-08-12 03:19:21

您不是唯一遇到此问题的人。我在 CGBitmapContextCreateImage() 方面遇到了重大问题。当您打开“僵尸”模式时,它甚至会警告您内存被释放两次(当情况并非如此时)。将 CG* 内容与 UI* 内容混合时肯定会出现问题。我仍在尝试找出如何解决这个问题的代码。

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

You're not the only one with this problem. I've had major problems with CGBitmapContextCreateImage(). When you turn on Zombie mode, it even warns you that memory is released twice (when it's not the case). There's definitely a problem when mixing CG* stuff with UI* stuff. I'm still trying to figure out how to code around this issue.

Side note: calling UIGraphicsBeginImageContext is not thread-safe. Be careful.

一紙繁鸢 2024-08-12 03:19:21

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

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

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

This really helped me! Here's how I used it to fix that nasty leak problem:

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

Notice, I had to store the CFDataRef (for a CFRelease(image->imageRef)) in my ~Image function. Hopefully this also helps others...JR

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文