AVAudioPlayer stops playing immediately with ARC

2019-01-07 23:36发布

I am trying to play an MP3 via AVAudioPlayer which I thought to be fairly simple. Unfortunately, it's not quite working. Here is all I did:

  • For the sake of testing, I created a new iOS application (Single View) in Xcode.
  • I added the AVFoundation framework to the project as well as the #import <AVFoundation/AVFoundation.h> to the ViewController.m

  • I added an MP3 File to the Apps 'Documents' folder.

  • I changed the ViewControllers viewDidLoad: to the following:

Code:

- (void)viewDidLoad
{
    [super viewDidLoad];        

    NSString* recorderFilePath = [NSString stringWithFormat:@"%@/MySound.mp3", [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"]];    

    AVAudioPlayer *audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:recorderFilePath] error:nil];
    audioPlayer.numberOfLoops = 1;

    [audioPlayer play];

    //[NSThread sleepForTimeInterval:20];
}

Unfortunately, the audio obviously stops right after it starts playing. If I uncomment the sleepForTimeInterval it plays for 20 seconds and stops afterwards. This problem occurs only when compiling with ARC, otherwise, it works flawlessly.

3条回答
三岁会撩人
2楼-- · 2019-01-08 00:08

If you need multiple AVAudioPlayers playing at the same time, create a NSMutableDictionary. Set the key as the filename. Remove from Dictionary via Delegate Callback like so:

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


    NSString* path = [[NSBundle mainBundle]
                      pathForResource:soundNum ofType:@"m4a"];
    NSURL* url = [NSURL fileURLWithPath:path];

    NSError *error = nil;
    AVAudioPlayer *audioPlayer =[[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];

    audioPlayer.delegate = self;

    if (_dictPlayers == nil)
        _dictPlayers = [NSMutableDictionary dictionary];
    [_dictPlayers setObject:audioPlayer forKey:[[audioPlayer.url path] lastPathComponent]];
    [audioPlayer play];

}

-(void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {        
   [_dictPlayers removeObjectForKey:[[player.url path] lastPathComponent]];
}
查看更多
不美不萌又怎样
3楼-- · 2019-01-08 00:11

Use the AVAudioPlayer as an ivar in the header file with strong:

@property (strong,nonatomic) AVAudioPlayer *audioPlayer
查看更多
我欲成王,谁敢阻挡
4楼-- · 2019-01-08 00:15

The problem is that when compiling with ARC you need to make sure to keep a reference to instances that you want to keep alive as the compiler will automatically fix "unbalanced" alloc by inserting release calls (at least conceptually, read Mikes Ash blog post for more details). You can solve this by assigning the instance to a property or a instance variable.

In Phlibbo case the code will be transformed into:

- (void)viewDidLoad
{
    [super viewDidLoad];        
    NSString* recorderFilePath = [NSString stringWithFormat:@"%@/MySound.mp3", [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"]];    
    AVAudioPlayer *audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:recorderFilePath] error:nil];
    audioPlayer.numberOfLoops = 1;
    [audioPlayer play];
    [audioPlayer release]; // inserted by ARC
}

And the AVAudioPlayer it will stop playing immediately as it gets deallocated when no reference is left.

I haven't used ARC myself and have just read about it briefly. Please comment on my answer if you know more about this and I will update it with more information.

More ARC information:
Transitioning to ARC Release Notes
LLVM Automatic Reference Counting

查看更多
登录 后发表回答