English 中文(简体)
页: 1 1. 产生AVAssetWriter图像的记忆泄漏
原标题:iOS Memory leaks creating a video from images with AVAssetWriter


I m new to both iOS development and to stackoverflow, so please bear with me if my code doesn t look as nice as it should.

I ve set up a test application using ARC and AVAssetWriter to create a video with images that live in my application bundle. Everything works as expected and the video gets created properly, but when I profile my application with Instruments I get memory leaks which I don t really know how to fix as I can t see anything in the Detail view which is related to my code (all the Leaked Objects are Malloc and the Responsible Library is VideoToolbox).

Here is the method I call to start writing the video in my view controller class:

- (void)writeVideo
{
    // Set the frameDuration ivar (50/600 = 1 sec / 12 number of frames)
    frameDuration = CMTimeMake(50, 600);
    nextPresentationTimeStamp = kCMTimeZero;

    [self deleteTempVideo];

    NSError *error = nil;
    AVAssetWriter *writer = [[AVAssetWriter alloc] initWithURL:[NSURL fileURLWithPath:self.videoPath] fileType:AVFileTypeQuickTimeMovie error:&error];
    if (!error) {
        // Define video settings to be passed to the AVAssetWriterInput instance
        NSDictionary *videoSettings = [NSDictionary dictionaryWithObjectsAndKeys:
                                       AVVideoCodecH264, AVVideoCodecKey, 
                                       [NSNumber numberWithInt:640],AVVideoWidthKey, 
                                       [NSNumber numberWithInt:480], AVVideoHeightKey, nil];
        // Instanciate the AVAssetWriterInput
        AVAssetWriterInput *writerInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:videoSettings];
        // Instanciate the AVAssetWriterInputPixelBufferAdaptor to be connected to the writer input
        AVAssetWriterInputPixelBufferAdaptor *pixelBufferAdaptor = [AVAssetWriterInputPixelBufferAdaptor assetWriterInputPixelBufferAdaptorWithAssetWriterInput:writerInput sourcePixelBufferAttributes:nil];
        // Add the writer input to the writer and begin writing
        [writer addInput:writerInput];
        [writer startWriting];
        [writer startSessionAtSourceTime:nextPresentationTimeStamp];
        //
        dispatch_queue_t mediaDataRequestQueue = dispatch_queue_create("Media data request queue", NULL);
        [writerInput requestMediaDataWhenReadyOnQueue:mediaDataRequestQueue usingBlock:^{
            while (writerInput.isReadyForMoreMediaData) {
                CVPixelBufferRef nextBuffer = [self fetchNextPixelBuffer];
                if (nextBuffer) {
                    [pixelBufferAdaptor appendPixelBuffer:nextBuffer withPresentationTime:nextPresentationTimeStamp];
                    nextPresentationTimeStamp = CMTimeAdd(nextPresentationTimeStamp, frameDuration);
                    CVPixelBufferRelease(nextBuffer);                    
                    dispatch_async(dispatch_get_main_queue(), ^{
                        NSUInteger totalFrames = [self.imagesNames count]; 
                        float progress = 1.0 * (totalFrames - [self.imageNamesCopy count]) / totalFrames;
                        [self.progressBar setProgress:progress animated:YES];
                    });
                } else {
                    [writerInput markAsFinished];
                    [writer finishWriting];
                    [self loadVideo];
                    dispatch_release(mediaDataRequestQueue);
                    break;
                }
            }
        }];
    }
}


And here is the method I use for fetching the pixel buffers to append to the pixel buffer adaptor instantiated in the previous method:

// Consume the imageNamesCopy mutable array and return a CVPixelBufferRef relative to the last object of the array
- (CVPixelBufferRef)fetchNextPixelBuffer
{
    NSString *imageName = [self.imageNamesCopy lastObject];
    if (imageName) [self.imageNamesCopy removeLastObject];
    // Create an UIImage instance
    UIImage *image = [UIImage imageNamed:imageName];
    CGImageRef imageRef = image.CGImage;    

    CVPixelBufferRef buffer = NULL;
    size_t width = CGImageGetWidth(imageRef);
    size_t height = CGImageGetHeight(imageRef);
    // Pixel buffer options
    NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
                             [NSNumber numberWithBool:YES], kCVPixelBufferCGImageCompatibilityKey, 
                             [NSNumber numberWithBool:YES], kCVPixelBufferCGBitmapContextCompatibilityKey, nil];
    // Create the pixel buffer
    CVReturn result = CVPixelBufferCreate(NULL, width, height, kCVPixelFormatType_32ARGB, (__bridge CFDictionaryRef) options, &buffer);
    if (result == kCVReturnSuccess && buffer) {
        CVPixelBufferLockBaseAddress(buffer, 0);
        void *bufferPointer = CVPixelBufferGetBaseAddress(buffer);
        // Define the color space
        CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
        // Create the bitmap context to draw the image
        CGContextRef context = CGBitmapContextCreate(bufferPointer, width, height, 8, 4 * width, colorSpace, kCGImageAlphaNoneSkipFirst);
        CGColorSpaceRelease(colorSpace);
        if (context) {
            CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);
            CGContextRelease(context);
        }
        CVPixelBufferUnlockBaseAddress(buffer, 0);
    }
    return buffer;
}
问题回答

我发现,记忆泄露与录像书写法无关。 泄漏代码似乎是<代码>[自删除] TempVideo] 方法一,请在-(避免)writeVideo

中,我仍需要说明其中的错误之处,但我猜测我的问题在目前范围上已经过时。 此处为<代码>-(撤销)deleteTempVideo:

- (void)deleteTempVideo
{
    NSFileManager *fileManager = [NSFileManager defaultManager];
    if ([fileManager isReadableFileAtPath:self.videoPath]) [fileManager removeItemAtPath:self.videoPath error:nil];
}

这里是我用来接触自觉的。 Path @property:

- (NSString *)videoPath
{
    if (!_videoPath) {
        NSString *fileName = @"test.mov";
        NSString *directoryPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
        _videoPath = [directoryPath stringByAppendingPathComponent:fileName];
    }
    return _videoPath;
}

As for my experience, When you use both arc and block, You should not directly use member variables. Instead, you can use weak properties.

Suppose you have a Class named ClassA and a member variable name _memberA. Usually I will define a property of memberA, then I may write code just like below.

__weak ClassA *weakSelf = self;
SomeVariable.function = ^() {
    weakSelf.memberA ..... (not use _memberA Here)
}

PS. If the block is from some static function just like [UIView animation: duration: completion:], it seems OK when you use member variable.

我以这种方式解决了许多记忆泄露问题。





相关问题
Finding leaks under GeneralBlock-16?

If ObjectAlloc cannot deduce type information for the block, it uses GeneralBlock . Any strategies to get leaks from this block that may eliminate the need of my trial and error methods that I use? ...

memory leaks appearing after multiple loading

I have an, at least to me, strange case here: I have a relatively simple program which loads an XML file from the web, parses it and write the data into an array of dictionaries. Each dictionary ...

iPhone memory management problems

I detach a thread calling my method which has a while-loop. Even though I have them marked as autoreleasepool, I release the objects manually, since the while-loop can continue on for a some time. ...

NSMutableArray and property leaking memory

I am in memory-leak cleanup mode on my latest app, and have come across something that I am unable to solve. the following method has been cleaned up except for 1 nagging issue. Instruments tells me ...

Core Data Instruments for the iPhone

I m trying to monitor my core data usage but all the Core Data instruments are greyed out in Instruments. It s also not available from the "Run with Performance Tool" in Xcode. It says that the Core ...

Memory Leak when using for(object in array) with iPhone SDK

I am running into some serious memory leaks in one of my applications I am building. I have a UINavigatonController that is inside a UITabBarview. Inside the NavView is a MKMap view. When you click an ...

热门标签