Question

I am in the process of writing an iPhone app, and am having a few problems with the memory. Here is the code below:

NSURL *url = [curItem valueForProperty: MPMediaItemPropertyAssetURL];

AVURLAsset *asset = [AVURLAsset URLAssetWithURL: url options:nil];

NSError *error = nil;

AVAssetReader* reader = [[AVAssetReader alloc] initWithAsset:asset error:&error];

AVAssetTrack* track = [[asset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0];

NSMutableDictionary* audioReadSettings = [NSMutableDictionary dictionary];
[audioReadSettings setValue:[NSNumber numberWithInt:kAudioFormatLinearPCM]
                     forKey:AVFormatIDKey];

AVAssetReaderTrackOutput* readerOutput = [AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:track outputSettings:audioReadSettings];

[reader addOutput:readerOutput];

[reader startReading];

CMSampleBufferRef sample = [readerOutput copyNextSampleBuffer];

while( sample != NULL)
{
    sample = [readerOutput copyNextSampleBuffer];

}
CFRelease(sample);

I am reading songs from the user's iTunes library (curItem is the current song), and if I leave the last line: CFRelease(sample) in the code, the program will stop - no error is shown - it just crashes. If I comment out the line, I of course run into memory problems, and the code crashes on about the fourth song after getting "Received memory warning."

What am I doing wrong?

Was it helpful?

Solution

The naming convention copyNextSampleBuffer implies that you own the object returned so you are correct to release it, but you are calling the copyNextSampleBuffer method multiple times in a loop and overwriting the previous copy without releasing it.

When you do finally call CFRelease, you are calling it on a variable that you have just checked to be NULL. According to this StackOverflow answer, calling CFRelease on NULL is not safe, so that's why you're crashing:

What you need to do instead is call release inside your while loop before you overwrite the variable, like this:

CMSampleBufferRef sample = [readerOutput copyNextSampleBuffer];

while( sample != NULL)
{
    CFRelease(sample);
    sample = [readerOutput copyNextSampleBuffer];
}

If that doesn't fix your crash (and even if it does), try running the static analyser over your code (select Analyze in the product menu in Xcode) and see if it reports any potential leaks or over-releases. Remember, every yellow and blue warning you get is a potential crash so try to fix them all.

EDIT: It just occurs to me that your loop doesn't make much sense - why are you reading samples over and over and then just throwing them away? Did you perhaps get the NULL check wrong in your while loop and you actually meant to write this instead?

CMSampleBufferRef sample = [readerOutput copyNextSampleBuffer];

while( sample == NULL)
{
    sample = [readerOutput copyNextSampleBuffer];
}

CFRelease(sample);

That should also be fine as in that case you are explicitly checking that the sample is not NULL before releasing it. Although you're still throwing away the sample before doing anything with it, and you also risk an infinite loop if the readerOutput contains no samples.

OTHER TIPS

Use either autorelease or ARC to get rid off the "too early release syndrom". In both cases the release-task is managed by someone else. For a new project I'd suggest ARC.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top