AVAudioPlayer – 你必须创build一个属性才能工作? (Xcode中)

这是我现在使用的代码,它不工作(当我按下调用此方法的button时,没有任何反应)。 以前,我有一个audioPlayer的属性,它的工作(显然,下面的所有audioPlayer是self.audioPlayer)。 问题是,当我试图播放声音两次,它会结束第一个声音播放。

这是不好的,因为我正在制作一个音板,并希望声音能够重叠。 我以为我可以让audioPlayer变成一个局部variables,而不是一个属性,一切都会好的,但现在声音根本不起作用,我不知道为什么。 在我为AVAudioPlayerfind的所有教程中,都有一个属性,但没有人解释为什么。 如果这不起作用,还有什么替代方法可以使声音重叠?

- (void)loadSound:(NSString *)sound ofType:(NSString *)type withDelegate:(BOOL)delegate { NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:sound ofType:type]]; AVAudioPlayer *audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil]; if (delegate) audioPlayer.delegate = self; [audioPlayer prepareToPlay]; [audioPlayer play]; } 

你需要一个财产或伊娃的原因是它提供了强有力的参考。 当使用ARC时,任何没有强指针的对象都是公平的游戏,实际上这就是你所看到的。

AVAudioPlayer强指针一次只允许一个audio播放器被引用,这也是正确的。

这个解决scheme,如果你select继续使用AVAudioPlayer就是使用某种集合对象来强有力地引用所有的播放器实例。 你可以使用一个NSMutableArray ,如下所示:

编辑我稍微调整了代码,所以播放声音的方法需要一个NSString soundName参数。

 @synthesize audioPlayers = _audioPlayers; -(NSMutableArray *)audioPlayers{ if (!_audioPlayers){ _audioPlayers = [[NSMutableArray alloc] init]; } return _audioPlayers; } -(void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag{ [self.audioPlayers removeObject:player]; } -(void)playSoundNamed:(NSString *)soundName{ NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:soundName ofType:@"wav"]]; AVAudioPlayer *audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil]; if (audioPlayer){ [audioPlayer setDelegate:self]; [audioPlayer prepareToPlay]; [audioPlayer play]; [self.audioPlayers addObject:audioPlayer]; } } 

一般来说, AVAudioPlayer对于音效/音板应用程序来说是矫枉过正的。 对于快速声音“下降”,您可能会findaudio工具箱框架,如我对这个问题的回答中所述。

从查看系统声音类参考,似乎一次只能播放一个声音。

它一次只能播放一个SystemSoundID 。 所以例如,如果你有soundOne和soundTwo。 在sound2正在播放时,您可以播放sound1,但是一次不能播放任何一个声音的多个实例。

能够播放可重叠的声音的最佳方式是什么?

最好的是意见。

如果你需要两个相同的声音同时播放 ,那么我会说在这个答案发布的代码将是使用的代码。 由于相同声音的每个重叠实例都需要创build一个新的资源,因此使用audioPlayerDidFinishPlaying:这样的代码更容易pipe理(内存很容易被回收)。

如果相同声音的重叠实例不是交易断路器,那么我认为只是使用AudioServicesCreateSystemSoundID()创build每个声音的一个实例是更有效的。

我绝对不会试图每按一次button来pipe理SystemSoundID的创build和处置。 那会匆匆出错。 在这种情况下, AVAudioPlayer仅仅是可维护性的明显赢家。

我假设你正在使用ARC。 audio播放器不起作用的原因是因为AVAudioPlayer对象正在被释放,然后在loadSound:方法终止后被销毁。 这是由于ARC的对象pipe理。 在ARC之前,代码:

 AVAudioPlayer *audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil]; if (delegate) audioPlayer.delegate = self; [audioPlayer prepareToPlay]; [audioPlayer play]; 

会发挥预期的声音。 但是,在loadSound:方法终止之后, AVAudioPlayer对象仍然存在很久。 这意味着每次你发出声音,你都会泄漏记忆。

有关属性的一些东西

引入了属性来减less开发人员编写和维护的代码量。 在属性之前,开发人员必须为每个实例variables编写setter和getters。 这是很多冗余的代码。 属性的一个幸运的副作用是,他们照顾了很多为基于对象的实例variables编写setter / getters所需的内存pipe理代码。 这意味着很多开发人员开始仅使用属性,即使对于不需要公开的variables也是如此。

由于ARC会为您处理所有的内存pipe理细节,因此只能将属性用于原始目的,从而减less冗余代码的数量。 传统的iVars默认会被强引用,这意味着一个简单的赋值,比如: title = [NSString stringWithFormat:@"hello"]; 基本上和代码一样: self.title = [NSString stringWithFormat:@"hello"];

好的,回到你的问题。

如果您要在loadSound:方法中创buildAVAudioPlayer实例,则需要对每个AVAudioPlayer实例保留强引用,否则ARC将会将其销毁。 我build议将新创build的AVAudioPlayer对象添加到NSMutableArray数组中。 如果您采用AVAudioPlayerDelegate协议,则可以实现audioPlayerDidFinishPlaying:successfully:方法。 您可以从数组中删除AVAudioPlayer对象,让ARC知道可以销毁该对象。