使用在GCD调度队列块外部定义的C指针对象(CGImageRef)时出现内存问题



我正在将磁盘中的图像读取到UIImage中,并在另一个工作队列块中使用其CGImage进行媒体处理。

现在,如果我在主队列中定义CGImageRef,并在工作队列中使用CGImageRef,我将获得EXC_BAD_ACCESS。像这样:

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
}];

如果我使用uiimage。CGImage直接在工作队列中,然后我没有得到异常。我认为这与ARC内存管理有关。UIImage是一个objective-c对象,CGImageRef是一个指向c结构的指针。在第一种情况下,UIImage是在我可以使用它的CGImage之前发布的。

我的问题是,如何在另一个调度队列中直接使用在主队列中定义的C指针?

出现此问题的原因,以及CGImageRetain()修复此问题的理由,是因为块未保留CGImage。这是因为CGImageRef不是Objective-C对象指针类型。只有当捕获的变量是Objective-C对象指针类型时,块才会自动保留这些变量。

因此,另一种解决方案是简单地让块捕获它的"对象指针类型"版本(所有Core Foundation对象都可以被视为Cocoa Objective-C对象,反之亦然):

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
}];

uiimage是自动释放的。因此,最有可能的是,当UIImage被自动释放时,cgImage正在被释放。

您可以尝试保留cgImage,然后在块中释放它。或者,您可以在块中使用uiimage(将[uiimage self];添加到块的末尾——看起来很傻,但有效)。

根本问题是所谓的内部指针问题。您正在获取指向对象内部某个对象的指针,但没有保持对该对象的强引用。当对象离开时,内部指针指向的任何对象都会被收割,您只剩下一个悬挂的指针。

我会选择这个:

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
}];

最新更新