如何在captureStillImageAsynchronouslyFromConnection的上下文中正确使用CFRelease?

时间:2013-07-11 09:34:37

标签: iphone automatic-ref-counting retain image-capture

对内存管理并不熟悉(最近才开始我的iOS旅程,我被ARC宠爱),我想成为一个好女孩,避免泄漏以及我知道如何。

我从AVFoundations captureStillImageAsynchronouslyFromConnection的视频连接捕获静止图像,因为我想访问图像字节。我也在使用Core Foundation和Core Graphics。

现在,当iOS完成捕获块并尝试释放对象时,我遇到了错误的访问异常。我一定是过分了。

认为我说得对:

  • 以正确的顺序发布了我创建的所有Core Graphics内容
  • 既未保留也未发布CVImageBufferRef,因为这只是 Get-something 并保留原始所有权
  • 发布了我的相机缓冲区元数据
  • 的副本

编辑:为我的示例代码添加了一项优化,简称Matthias BauchCFDataCreateWithBytesNoCopy的内存处理出错,我CFRelease现在正确。这不会导致问题。

编辑2:感谢Matthias Bauch,我已经能够将其缩小到一个被调用的方法。除了其他所有内容,但是这种方法,我可以制作尽可能多的快照,没有例外。我在调用它的captureStillImageAsynchronouslyFromConnection块下面添加了那个代码。我将继续以这种方式找出问题所在......

编辑3:在被调用的方法中,发布了2件我不负责的东西。感谢newacct,他一点一点地解释了它,我现在有了一种工作方法。我发布下面的工作代码。

带有块的

captureStillImageAsynchronouslyFromConnection

[[self imageOutput] captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler: ^(CMSampleBufferRef imageSampleBuffer, NSError *error)
    { // get time stamp for image capture
         NSDate *timeStamp = [NSDate date];

         //get all the metadata in the image
         CFDictionaryRef metadata = CMCopyDictionaryOfAttachments(kCFAllocatorDefault,
                                                                  imageSampleBuffer,
                                                                  kCMAttachmentMode_ShouldPropagate);

         // get image reference
         CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(imageSampleBuffer);

         // >>>>>>>>>> lock buffer address
         CVPixelBufferLockBaseAddress(imageBuffer, 0);

         //Get information about the image
         uint8_t *baseAddress = (uint8_t *)CVPixelBufferGetBaseAddress(imageBuffer);
         size_t dataSize = CVPixelBufferGetDataSize(imageBuffer);
         size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer);
         size_t width = CVPixelBufferGetWidth(imageBuffer);
         size_t height = CVPixelBufferGetHeight(imageBuffer);

         // create a pointer to the image data
         CFDataRef rawImageBytes = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, baseAddress, dataSize, kCFAllocatorNull);

         // create the color space for the current device
         CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

         //Create a bitmap context
         CGContextRef newContext = CGBitmapContextCreate(baseAddress, width, height, 8, bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst);

         // <<<<<<<<<< unlock buffer address
         CVPixelBufferUnlockBaseAddress(imageBuffer, 0);

         // release core graphics object
         CGColorSpaceRelease(colorSpace);

         // Create a bitmap image from data supplied by the context.
         CGImageRef newImage = CGBitmapContextCreateImage(newContext);

         // release core graphics object
         CGContextRelease(newContext);

         BOOL saved = FALSE;

         // save CGImage as TIFF file with Objective-C
         saved = [[self photoIOController] writeToSubdirectoryWithImageRef:newImage orientation:[self orientation] timeStamp: timeStamp andMetadata: metadata];

         // create UIImage (need to change the orientation of the image so that the image is displayed correctly)
         UIImage *image= [UIImage imageWithCGImage:newImage scale:1.0 orientation:UIImageOrientationRight];

         // release core graphics object
         CGImageRelease(newImage);

         // set property for display in StillImageViewController
         [self setStillImage: image];

         // release core foundation object
         CFRelease(rawImageBytes);

         // release core foundation object
         CFRelease(metadata);

         // send notification for the camera container view controller when the image has been taken
         [[NSNotificationCenter defaultCenter] postNotificationName:kImageCapturedSuccessfully object:nil];
    }];

似乎导致异常的方法:

  1. 参数:

    • imageRef CGImageRef newImage = CGBitmapContextCreateImage(newContext);

    • 方向是来自UIDeviceOrientation的{​​{1}},已过滤减去我未做出反应的

    • timeStamp UIDeviceOrientationDidChangeNotification

    • 元数据NSDate *timeStamp = [NSDate date];

  2. 代码:

  3. CFDictionaryRef metadata = CMCopyDictionaryOfAttachments(kCFAllocatorDefault, imageSampleBuffer, kCMAttachmentMode_ShouldPropagate);

1 个答案:

答案 0 :(得分:2)

Core Foundation对象的内存管理与Cocoa中Objective-C对象的内存管理完全一样 - 如果保留它,则必须释放它;如果你没有保留它,你不能释放它。命名约定略有不同。虽然Cocoa中的“保留”方法的名称以allocretainnewcopymutableCopy开头,但在Core Foundation中,如果函数名称包含CreateCopy

因此,考虑到这一点,让我们看看你的代码。

在第一段代码中:

  • metadata:从Copy返回,因此保留,所以必须发布
  • imageBuffer:从Get而不是CopyCreate的函数返回,因此不会保留,因此不得发布
  • rawImageBytes:从Create返回,因此保留,所以必须发布
  • colorSpace:从Create返回,因此保留,所以必须发布
  • newContext:从Create返回,因此保留,所以必须发布
  • newImage:从Create返回,因此保留,所以必须发布

writeToSubdirectoryWithImageRef:

  • imageReforientationmetadata:它们是参数,所以很明显你没有保留它,所以你不能发布它
  • mutableMetadata:从Create返回,因此保留,因此您必须将其释放
  • myKey:未保留,因此不得发布
  • myValue:从Create返回,因此保留,因此发布
  • url:它直接桥接(不是桥接保留或桥接传输),方法名称没有任何保留前缀,因此不会保留,因此不得发布
  • destination:从Create返回,因此保留,因此发布

请参阅?这一切都很简单。