views:

1063

answers:

2

I posted a much longer question a few minutes ago, and as it usually goes as soon as I posted it I realized what was going on, so I deleted it since most of the post was irrelevant. Then I went back to Google.

Turns out I'm having almost the same exact problem as described in this post, unanswered from June. http://www.iphonedevsdk.com/forum/iphone-sdk-development/20975-avaudioplayer-nsurl-memory-management.html

In summary: I'm using AVAudioPlayer and releasing it with the audioPlayerDidFinishPlaying:successfully: delegate method. After I initialize the player, its associated NSURL object needs to be freed, otherwise it leaks. But when I release it after initializing the player, it crashes since it has already been freed. The weird thing is that it doesn't always crash the first time, most of the time it crashes after the second sound has been played. Sometimes (rarely) it will allow a handful or players to be alloc/released (I reuse the pointers after freeing) before crashing. Any help?

Code snippet: (soundKeyUp is an AVAudioPlayer* class variable, hence no declaration here)

NSString *soundKeyUpPath = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:@"soundKeyUp%d", key.tag % 5] ofType:@"wav"];
    NSURL *soundKeyUpURL = [[NSURL alloc] initFileURLWithPath:soundKeyUpPath];
    soundKeyUp = [[AVAudioPlayer alloc] initWithContentsOfURL:soundKeyUpURL error:nil];
    if(soundKeyUp) {
        [soundKeyUp setDelegate:self];
        [soundKeyUp play];
    }
    else {
        [soundKeyUp release];
        soundKeyUp = nil;
    }

    [soundKeyUpPath release];
    [soundKeyUpURL release];

Response to Steve Riggins: The trick of it is that it doesn't crash at the same time every time, as mentioned. It almost always properly releases the first time (or at least, it doesn't crash or leak) but usually after the second time I allocate/release the player and URL, it crashes on releasing the URL. Sometimes, it goes 3, 4, 5+ times before crashing, but it always does.

+1  A: 

Release the URL when you create the player. I do this in my app and it functions fine. No leaks, crashes, etc

AVAudioPlayer *newPlayer =
        [[AVAudioPlayer alloc] initWithContentsOfURL: fileURL
                                               error: nil];
        [fileURL release];

And never, ever release objects that are contained by other objects. That is their job, not yours.

Edit: If your url is still leaking, you need to look at what you're doing when you create it. It should be something like:

NSURL *fileURL = [[NSURL alloc] initFileURLWithPath: [self path]];

Following the Cocoa rules of ownership, using an alloc method means you own it, so you release it.

Steve Riggins
I've updated my post with my actual code and a direct response, since I couldn't fit it in the comment box. I *do* release the URL when I create the player, the edit I made to the delegate method was just a test to see what was going on at that point in the program. And right, I know not to release objects contained by other objects, but 1) I wasn't aware before looking it up that AVAudioPlayer kept the NSURL as a property after initialization, so I was curious if/how AVAudioPlayer handled it; and 2) attempting to break things is a great way to learn how they work.
Dan R.
Agreed. You should never, ever use anything that looks like this: `[[something methodName] release]`. It's just crazy memory bugs waiting to happen.
Squeegy
else { [soundKeyUp release]; soundKeyUp = nil; }You certainly don't need this. If the object is nil, no need to release it.You do need to release it in dealloc. I'd use a retain property for this ivar so that in dealloc you can simple self.soundKeyUp = nil.@property(nonatomic, retain) AVAudioPlayer *soundKeyUp;then in implementation @synthesize soundKeyUp.then self.soundKeyUp = [[AVAudioPlayer alloc].... etcYou are likely making a couple of mistakes that are making it tough to debug. Be careful to understand every stage of every allocation.
Steve Riggins
A: 
 - (IBAction) playaction {

        NSString *soundFilePath = [[NSBundle mainBundle] pathForResource:@"songname" ofType:@"mp3"];
        NSURL *newURL = [[NSURL alloc] initFileURLWithPath: soundFilePath];
        self.soundFileURL = newURL;
        [newURL release];
        [[AVAudioSession sharedInstance] setDelegate: self];
        [[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryAmbient error: nil];

    // Registers the audio route change listener callback function
    AudioSessionAddPropertyListener (
                                     kAudioSessionProperty_AudioRouteChange,
                                     audioRouteChangeListenerCallback,
                                     self
                                     );

    // Activates the audio session.

    NSError *activationError = nil;
    [[AVAudioSession sharedInstance] setActive: YES error: &activationError];

    AVAudioPlayer *newPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL: soundFileURL error: nil];
    self.appSoundPlayer = newPlayer;
    [newPlayer release];
    [appSoundPlayer prepareToPlay];
    [appSoundPlayer setVolume: 1.0];
    [appSoundPlayer setDelegate: self];
    [appSoundPlayer play];

}

You will be required to use something similar to this. Here appSoundPlayer is declared in the header file as

AVAudioPlayer *appSoundPlayer;

Also set property to it,

@property(nonatomic, retain)AVAudioPlayer *appSoundPlayer;

Release it in the dealloc method.. This is the method i'm following and haven't seen any memory leaks

Nithin