对内存管理不是很熟悉(最近才开始进入 iOS 的旅程,我被 ARC 宠爱了),我想成为一个好女孩,并且我知道如何避免泄漏。
我从与 AVFoundations 的视频连接中捕获静止图像captureStillImageAsynchronouslyFromConnection
,因为我想访问图像字节。我也在使用 Core Foundation 和 Core Graphics。
现在,当 iOS 完成捕获块并尝试释放对象时,我得到了一个错误的访问异常。我一定做得过火了。
我想我有正确的:
- 以正确的顺序发布了我创建的所有核心图形东西
- 既没有保留也没有释放 CVImageBufferRef,因为这只是一个Get-something并保留了原始所有权
- 发布了我的相机缓冲区元数据副本
编辑:向我的示例代码添加了优化,马蒂亚斯·鲍赫(Matthias Bauch )的谄媚:内存处理中出现错误CFDataCreateWithBytesNoCopy
,我 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];
}];
似乎导致异常的方法:
参数:
imageRef是
CGImageRef newImage = CGBitmapContextCreateImage(newContext);
方向是
UIDeviceOrientation
来自UIDeviceOrientationDidChangeNotification
,过滤减去我没有反应的那些时间戳是
NSDate *timeStamp = [NSDate date];
元数据是
CFDictionaryRef metadata = CMCopyDictionaryOfAttachments(kCFAllocatorDefault, imageSampleBuffer, kCMAttachmentMode_ShouldPropagate);
编码:
-(BOOL) writeToSubdirectoryWithImageRef: (CGImageRef) imageRef orientation: (UIDeviceOrientation) orientation timeStamp: (NSDate*) timeStamp andMetadata: (CFDictionaryRef) metadata
{
int imageOrientation;
// According to Apple documentation on key kCGImagePropertyOrientation,
/* http://developer.apple.com/library/ios/documentation/GraphicsImaging/Reference/CGImageProperties_Reference/Reference/reference.html#//apple_ref/doc/uid/TP40005103-CH3g-SW37 */
// the values are the same as in TIFF and EXIF (so I use the libtiff definitions)
switch (orientation) {
case UIDeviceOrientationPortrait:
imageOrientation = ORIENTATION_RIGHTTOP;
break;
case UIDeviceOrientationPortraitUpsideDown:
imageOrientation = ORIENTATION_LEFTBOT;
break;
case UIDeviceOrientationLandscapeLeft:
imageOrientation = ORIENTATION_TOPLEFT;
break;
case UIDeviceOrientationLandscapeRight:
imageOrientation = ORIENTATION_BOTRIGHT;
break;
default:
imageOrientation = ORIENTATION_RIGHTTOP;
break;
}
// mutable metadata copy
CFMutableDictionaryRef mutableMetadata = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, CFDictionaryGetCount(metadata), metadata);
// set the key-value-pair
CFStringRef myKey = kCGImagePropertyOrientation; // do not release!
CFTypeRef myValue = CFNumberCreate(NULL, kCFNumberIntType, &imageOrientation);
CFDictionaryReplaceValue(mutableMetadata, myKey, myValue);
// get the time stamp
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:kDermaAppPhotoTimeStampFormat];
NSString *sTimeStamp = [dateFormatter stringFromDate:timeStamp];
if ([self pathDermaAppSubdirectory] != nil)
{
NSString *filePath = [NSString stringWithFormat:(@"%@/%@%@"),[self pathDermaAppSubdirectory],sTimeStamp,kTIFFImageNameEnding];
// log file path
HLSLoggerDebug(@"tiff image filePath = %@",filePath);
CFURLRef url = (__bridge CFURLRef)[NSURL fileURLWithPath:filePath isDirectory:NO];
CGImageDestinationRef destination = CGImageDestinationCreateWithURL(url, kUTTypeTIFF, 1, NULL); // bridge: do not release!
CGImageDestinationAddImage(destination, imageRef, mutableMetadata);
if (CGImageDestinationFinalize(destination))
{
[self setPathLastImageSave:filePath];
// release core foundation object
CFRelease(destination);
// release core foundation object
CFRelease(mutableMetadata);
// release core foundation object
CFRelease(myValue);
return TRUE;
}
else
{
[self setPathLastImageSave:nil];
HLSLoggerFatal(@"Failed to write image to %@", filePath);
}
// release core foundation object
CFRelease(destination);
}
// release core foundation object
CFRelease(mutableMetadata);
// release core foundation object
CFRelease(myValue);
return FALSE;
}