收录日期:2019/04/24 08:28:26 时间:2010-06-16 01:56:25 标签:iphone,avaudioplayer,exc-bad-access

I am trying to use AVAudioPlayer to play some sounds in quick succession. When I invoke the sound-playing function less frequently so that the sounds play fully before the function is invoked again, the application runs fine. But if I invoke the function quickly in rapid succession (so that sounds are played while the previous sounds are still being played), the app eventually crashes after ~20 calls to the function, with the message "EXC_BAD_ACCESS". Here is code from the function:

NSString *nsWavPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:wavFileName];

AVAudioPlayer* theAudio = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:nsWavPath] error:NULL];
theAudio.delegate = self;

[theAudio play];

As mentioned in another thread, I implemented the following delegate function:

- (void) audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
    if(!flag)
        NSLog(@"audio did NOT finish successfully\n");
    [player release];
}

But the app still crashes after around ~20 rapid calls to the function. Any idea what I'm doing wrong?

First off: Make sure you're not trying to play a sound a second time that has already been released via [player release]; If you are, you'll get that error message right away. Once you release the player associated with a specific sound, you cannot play the file again like you have shown here. Try commenting out that line of code and see if it still happens.

I've also run into an issue where AVAudioPlayer allocates 32kb everytime a new player is created and if you have enough sounds you can run out of memory and get a crash. I highly doubt this is your problem though since it doesn't typically throw that error code.

EXC_BAD_ACCESS is typically from trying to access a pointer that no longer exists (such as your player object) for most of the cases I've seen on this forum

I found a better solution to this problem. You simply need to active/deactivate your AVAudioSession.

For example: In you viewDidLoad method put this:

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

Then, you can play a sound in whatever method like this:

... some awesome code ...
self.audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:soundFileURL error:nil];
[self.audioPlayer setDelegate:self];
[self.audioPlayer setVolume: 0.7f];
[self.audioPlayer setNumberOfLoops:0];
if ([self.audioPlayer prepareToPlay]) {
    [self.audioPlayer play];            
}
....

And finally, (no matters if sound was finished playing), when you're done with sounds then just do this:

// I've made it on the view's callback
- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];

    [self.audioPlayer stop];
    [self setAudioPlayer:nil];
    NSError *activationError = nil;
    [[AVAudioSession sharedInstance] setActive:NO
                                         error:&activationError];
    .... etc, etc...
}

And that's all :) Your app will never crash again with an EXC_BAD_ACCESS.

Hope this helps you and avoid that hacky [self retain] / [self release]

From the Apple's Audio Session Programming Guide: https://developer.apple.com/library/ios/documentation/Audio/Conceptual/AudioSessionProgrammingGuide/ConfiguringanAudioSession/ConfiguringanAudioSession.html