Skip Navigation

Ramblings from the team at zinc Roe

Metal Fish Eggs

AVAudioPlayer Memory Leak

While coding the sound for Arctic Shuffle 2, I ran into a non-obvious memory (to me, anyway) memory leak related to the AVAudioPlayer that took forever to track down.

Here was my original code, which leaks if an error occurs in the initWithData:error: method:

- (AVAudioPlayer *)audioPlayerWithContentsOfFile:(NSString *)path {
    NSData *data = [NSData dataWithContentsOfFile:path];
    return [[[AVAudioPlayer alloc] initWithData:audioData error:NULL] autorelease];
}

If an error occurs in the initWithData:error: method (say because the file is missing), it returns nil. If it returns nil, autorelease gets called on nil, not on the AVAudioPlayer object, and the AVAudioPlayer object gets leaked.

Here’s the leak-free version:

- (AVAudioPlayer *)audioPlayerWithContentsOfFile:(NSString *)path {
    NSData *data = [NSData dataWithContentsOfFile:path];
    AVAudioPlayer *player = [AVAudioPlayer alloc];
    if([player initWithData:audioData error:NULL]) {
        [player autorelease];
    } else {
        [player release];
        player = nil;
    }
    return player;
}

The leak-free version stores the pointer returned by alloc, rather than the pointer returned by initWithData:error:. That way, whatever happens, the player can still be released.

10 comments on AVAudioPlayer Memory Leak
  1. PJ Cabrera Says:

    Thanks for this, you saved me and countless others a bunch of time!

  2. shiva Says:

    Hi Luke can u post full application. When i use ur function
    audioPlayerWithContentsOfFile:(NSString *)path no leak. But i am unable to hear any sound. I commented [player release] and [player autorelease]. Then i am able to hear sound but same memory leak.

    Could you please solve my problem.

    Thanks
    shiva

  3. Luke Says:

    @shiva: You just need to retain the AVAudioPlayer object returned by audioPlayerWithContentsOfFile:

    AVAudioPlayer *player = [someObject audioPlayerWithContentsOfFile:@"someFile.caf"];
    [player retain];

    And then release it when you’re done with it :-)

  4. shiva Says:

    Still i am getting leak.

    I am using more than one sound file.
    When even new image scrolls onto view, i am playing different sound

    Could you please help me?

    Thanks
    shiva

  5. Gene De Lisa Says:

    >>> player initWithData:audioData

    Don’t you mean data since you said NSData *data = …

    And don’t you have to release that too?

  6. Gerardo Says:

    Not solved entirely the problems on my App, but your trick is a BIG step ahead!
    Before my Intro animated with sounds crashed after 3 times you launch on buggy iOs 4.02 with iPhone 3G (worst environment), now lasts 6 time !

  7. zmip Says:

    Someone (you?) posted your code on StackOverflow:

    http://stackoverflow.com/questions/2124622/trying-to-fix-memory-leak-using-avaudioplayer

    …and got told that it’s pretty crappy altogether…

  8. Luke Says:

    @Gene De Lisa: The NSData object returned by [NSData dataWithContentsOfFile:] is already auto-released.

    @zmip: The code posted on Stack Overflow doesn’t mention that it’s written that way to work around a problem caused by calling alloc and init on the same line. I realize that what I’m suggesting isn’t the standard way to allocate and initialize an object, but it was necessary to get rid of the memory leak I had.

  9. jon Says:

    thanks. this saved my christmas.

  10. Ivan Vučica Says:

    @Luke:
    Regarding zmip’s link, I posted a link to this article on that StackOverflow answer, to provide people context.

Your Comment…

You can use these tags: <a> <blockquote> <strong> <em> <strike> <code> <pre> Use <pre lang="LANGUAGE"> for syntax highlighted code blocks.