メモリ管理にはあまり詳しくありません (最近 iOS への旅を始めたばかりで、ARC に甘やかされています)、私はいい子になりたいと思っていました。
captureStillImageAsynchronouslyFromConnection
画像バイトにアクセスしたいので、 AVFoundations とのビデオ接続から静止画像をキャプチャします。Core Foundation と Core Graphics も使用しています。
iOS がキャプチャ ブロックを終了し、オブジェクトを解放しようとすると、不正なアクセス例外が発生します。やり過ぎたに違いない。
私は正しく持っていると思います:
- 私が作成したすべての Core Graphics のものを正しい順序でリリースしました
- CVImageBufferRef を保持も解放もしていません。これは単なるGet-somethingであり、元の所有権を保持しているためです。
- カメラ バッファ メタデータのコピーをリリースしました
編集: Matthias Bauch の厚意により、私のサンプル コードに最適化を追加しましたCFDataCreateWithBytesNoCopy
: のメモリ処理にエラーがありましたCFRelease
。これは問題の原因ではありませんでした。
EDIT 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
fromUIDeviceOrientationDidChangeNotification
であり、フィルタリングされたものから、私が反応しないものを差し引いたものですタイムスタンプは
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;
}