KevinTydlacka
KevinTydlacka

Reputation: 1293

Over-release of AVAudioPlayer instance

Having a really hard time hunting this one down. A production app is crashing intermittently (~2% of users). It's a card game so there is a lot of tapping on cards. Whenever you tap on a card it makes a sound. The crash happens seemingly randomly, but I can reproduce by tapping on cards for a while and then randomly it will just crash.

It's not possible to reproduce reliably, but after long sessions using Instruments/Zombies I see that the Zombie object is an AVAudioPlayer object. It is a simple card game app (not done using SK or any other game framework--all UIKit/normal Objective C). I'm using a very straightforward implementation of AVAudioPlayer. All details below.

Stack Trace:

Crashed: com.apple.main-thread
0  libobjc.A.dylib                0x206270d70 objc_msgSend + 16
1  Foundation                     0x207b3d42c __NSThreadPerformPerform + 336
2  CoreFoundation                 0x20701a0e0 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 24
3  CoreFoundation                 0x20701a060 __CFRunLoopDoSource0 + 88
4  CoreFoundation                 0x207019944 __CFRunLoopDoSources0 + 176
5  CoreFoundation                 0x207014810 __CFRunLoopRun + 1040
6  CoreFoundation                 0x2070140e0 CFRunLoopRunSpecific + 436
7  GraphicsServices               0x20928d584 GSEventRunModal + 100
8  UIKitCore                      0x2343a8c00 UIApplicationMain + 212
9  <app name>                      0x1007930f0 main (main.m:14)
10 libdyld.dylib                  0x206ad2bb4 start + 4

AppDelegate.m didFinishLaunchingWithOptions

[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient error:nil];
BOOL success = [[AVAudioSession sharedInstance] setActive:YES error:nil];

ViewController.h

@property (strong, nonatomic) AVAudioPlayer *audioPlayer;

ViewController.m

-(void) playSound:(NSString *) strSoundName {

    NSString *fileName = @"Move_1";
    NSString *soundFilePath = [[NSBundle mainBundle] pathForResource:fileName ofType:@"aif"];
    NSURL *soundFileURL = [NSURL fileURLWithPath:soundFilePath];

    float volume = 0.5;

    self.audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:soundFileURL error:nil];
    [self.audioPlayer setVolume:volume];
    [self.audioPlayer play];
}

AVAudioPlayer Reference History enter image description here

handleTap::: is the method that calls playSound

Upvotes: 2

Views: 168

Answers (2)

KevinTydlacka
KevinTydlacka

Reputation: 1293

Was never able to figure out exactly why it was crashing randomly.

However, we were able to resolve the issue by initializing the AVAudioPlayer once in viewDidLoad and then just call play in that method (instead of initializing every time we wanted to play the sound).

Upvotes: 0

Mattie
Mattie

Reputation: 3028

You've done a pretty fantastic job debugging here. Most people get very tripped up by memory management/heap corruption bugs, like this one.

There is, however, a clue in that Instruments trace that you need to investigate: the zombie turns up just after a performSelector call. This makes me think that a pointer to your view controller, or to the AVAudioPlayer instance itself, is being referenced in the code that performSelector invokes. Since this code is running after the view controller has been deallocated, you get your dangling pointer.

For starters, I'd look more closely at what handleTap::: is doing. Are you invoking performSelector there? And, if so, take a close look at what instances you are using for it. If not, audit your uses of performSelector in other places to see if somehow a plain pointer could be captured to your view controller or AVAudioPlayer instance.

Upvotes: 1

Related Questions