0

メモリ管理にはあまり詳しくありません (最近 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];
    }];

例外の原因と思われるメソッド:

  1. パラメータ:

    • imageRefCGImageRef newImage = CGBitmapContextCreateImage(newContext);

    • オリエンテーションUIDeviceOrientationfromUIDeviceOrientationDidChangeNotificationであり、フィルタリングされたものから、私が反応しないものを差し引いたものです

    • タイムスタンプNSDate *timeStamp = [NSDate date];

    • メタデータCFDictionaryRef metadata = CMCopyDictionaryOfAttachments(kCFAllocatorDefault, imageSampleBuffer, kCMAttachmentMode_ShouldPropagate);

  2. コード:


-(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;
}
4

1 に答える 1

2

Core Foundation オブジェクトのメモリ管理は、Cocoa の Objective-C オブジェクトのメモリ管理とまったく同じです。保持している場合は解放する必要があります。保持しなかった場合は、解放してはなりません。命名規則はわずかに異なります。Cocoa の「保持」メソッドの名前はallocretainnewcopy、およびmutableCopyで始まりますが、Core Foundation では、関数名にCreateまたはが含まれている場合Copyです。

それを念頭に置いて、コードを見てみましょう。

コードの最初の部分:

  • metadata: から返されCopyたため、保持されているため、解放する必要があります
  • imageBufferGet: 、notCopyまたはを使用して関数から返されるCreateため、保持されないため、解放してはなりません
  • rawImageBytes: から返されCreateたため、保持されているため、解放する必要があります
  • colorSpace: から返されCreateたため、保持されているため、解放する必要があります
  • newContext: から返されCreateたため、保持されているため、解放する必要があります
  • newImage: から返されCreateたため、保持されているため、解放する必要があります

writeToSubdirectoryWithImageRef:

  • imageReforientation、およびmetadata: パラメータなので、保持していないことは明らかなので、解放してはいけません
  • mutableMetadata: から返されCreateたため、保持されているため、解放する必要があります
  • myKey: 保持されないため、解放してはなりません
  • myValue: から返されCreateたので、保持されているので解放します
  • url: 直接ブリッジされ (bridge-retained または bridge-transfered ではなく)、メソッド名に保持プレフィックスがないため、保持されないため、解放してはなりません
  • destination: から返されCreateたので、保持されているので解放します

見る?とても簡単です。

于 2013-07-13T02:23:50.733 に答える