X.Y.
X.Y.

Reputation: 13916

Memory issue with using a C pointer object (CGImageRef) defined outside in a GCD dispatch queue block

I'm reading an image from disk into a UIImage, and using its CGImage in another worker queue block for media processing.

Now I'm getting EXC_BAD_ACCESS if I define a CGImageRef in the main queue, and use the CGImageRef in the worker queue. Like this:

UIImage *uiimage = [UIImage imageWithContentsOfFile:[aPhoto completeLargeThumbFilePath]];

CGImageRef cgImage = uiimage.CGImage;

dispatch_queue_t    dispatchQueue = dispatch_queue_create("mediaInputQueue", NULL);
[writerInput requestMediaDataWhenReadyOnQueue:dispatchQueue usingBlock:^{

    // ... unrelated stuff
    CVPixelBufferRef buffer = (CVPixelBufferRef)[self pixelBufferFromCGImage:cgImage size:size];
    // ... unrelated stuff
}];

If I use uiimage.CGImage directly in the worker queue, then I don't get the exception. I think this is related to the ARC memory management. UIImage is an objective-c object, and CGImageRef is a pointer to an C struct. The UIImage is released before I can use its CGImage in the first case.

My question is how can I directly use a C pointer defined in main queue in another dispatch queue?

Upvotes: 2

Views: 883

Answers (2)

bbum
bbum

Reputation: 162712

The uiimage is autoreleased. Thus, most likely, the cgImage is being deallocated when the UIImage is autoreleased.

You could try retaining the cgImage and then releasing it in the block. Or you could use the uiimage in the block (add [uiimage self]; to the end of the block -- looks goofy, but works).

The underlying issue is what is called the interior pointer problem. You are grabbing a pointer to something inside an object, but not maintaining a strong reference to the object. When the object goes away, whatever the interior pointer points to is reaped and you are left with a dangling pointer.

I'd go with this:

UIImage *uiimage = [UIImage imageWithContentsOfFile:[aPhoto completeLargeThumbFilePath]];

dispatch_queue_t    dispatchQueue = dispatch_queue_create("mediaInputQueue", NULL);
[writerInput requestMediaDataWhenReadyOnQueue:dispatchQueue usingBlock:^{
    CGImageRef cgImage = uiimage.CGImage;

    // ... unrelated stuff
    CVPixelBufferRef buffer = (CVPixelBufferRef)[self pixelBufferFromCGImage:cgImage size:size];
    // ... unrelated stuff
}];

Upvotes: 2

newacct
newacct

Reputation: 122449

The reason you have this problem, and the reason that CGImageRetain() fixes it, is because the CGImage is not retained by the block. This is because CGImageRef is not an Objective-C object pointer type. Blocks only automatically retain captured variables when they are Objective-C object pointer types.

So an alternate solution is to simply let the block capture an "object pointer type" version of it (all Core Foundation objects can be treated as Cocoa Objective-C objects, and vice versa):

UIImage *uiimage = [UIImage imageWithContentsOfFile:[aPhoto completeLargeThumbFilePath]];

CGImageRef cgImage = uiimage.CGImage;

id cgImageAsObject = (__bridge id)cgImage;

dispatch_queue_t    dispatchQueue = dispatch_queue_create("mediaInputQueue", NULL);
[writerInput requestMediaDataWhenReadyOnQueue:dispatchQueue usingBlock:^{

    // ... unrelated stuff
    CGImageRef cgImageAgain = (__bridge CGImageRef)cgImageAsObject;
    CVPixelBufferRef buffer = (CVPixelBufferRef)[self pixelBufferFromCGImage:cgImageAgain size:size];
    // ... unrelated stuff
}];

Upvotes: 3

Related Questions