1

The origin of this question stems from the answer to this question.

I have a series of puzzles that are managed by their own ViewControllers. The workflow looks like this:

UINavigationViewController-->Menu View Controller--> Routing View Controller --> Randomly Selected Puzzle View Controller

The "Routing View Controller" randomly selects a puzzle view controller to push onto the stack. Once someone has solved the puzzle I play a "You Win" audio sample and then pop the view controller off the stack. At that point Routing View Controller randomly selects the next puzzle. Here's my code for playing the sound and for popping the puzzle view controller off the stack:

-(void) playSound:(SystemSoundID)soundID withClientData:(UIViewController*)clientData {
     soundIsPlaying = true;

     if (clientData)
         AudioServicesAddSystemSoundCompletion(soundID, nil, nil, soundFinishedPlaying, (__bridge void *)clientData);
     else
         AudioServicesAddSystemSoundCompletion(soundID, nil, nil, soundFinishedPlaying, nil);

     AudioServicesPlaySystemSound(soundID);
} 

void soundFinishedPlaying(SystemSoundID ssID, void *clientData)
{
    soundIsPlaying = false;

    if(clientData) {
        NSLog(@"clientData not null!");
        UINavigationController * navController = ((__bridge UIViewController *) clientData).navigationController;
        if (navController){
            NSLog(@"navController not null!");
            [navController popViewControllerAnimated:false];
        } else {
            NSLog(@"NavController was nil!");
        }
     } 

}

The problem is after the second puzzle is solved I get a EXC_BAD_ACCESS error thrown on this line of code within soundFinishedPlaying:

UINavigationController * navController = ((__bridge UIViewController *) clientData).navigationController;

If I debug this line of code I can see that clientData has a reference to a UINavigationController so I'm a bit perplexed as to why I'm seeing this error.

I suspect it might have something to do with the fact I need to specify a straight C function for soundFinishedPlaying and/or the fact I need to pass in the UIViewController reference as a void *.

Please let me know where I've gone wrong or if there's a better way to trigger a view transition once an audio clip has completed playing. Thanks in advance!

Community
  • 1
  • 1
JJensL
  • 149
  • 1
  • 13

1 Answers1

1

I figured out what's going on here. The problem was with the "bridge" casting I did on this line:

AudioServicesAddSystemSoundCompletion(soundID, nil, nil, soundFinishedPlaying, (__bridge void *)clientData);

If I changed the bridge mode to __bridge_retained instead that insured that my instance of NavigationController would not be dereferenced until after the sound completion method had completed. By using __bridge_retained that also means I have to explicitly dereference the point using CFRelease(...) in soundFinishedPlaying.

I'm still ramping up on the Apple code analysis tooling to confirm there are no other memory leaks so if I'm missing any details here feel free to fill them in.

FYI...thanks to this post for pointing me in the right direction.

Community
  • 1
  • 1
JJensL
  • 149
  • 1
  • 13