AVAudioPlayer - 你必须创建一个属性才能工作吗?(Xcode中)

rob*_*ino 2 audio xcode objective-c avaudioplayer ios

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

这不好,因为我正在制作一个音板,并希望声音能够重叠.我以为我可以让audioPlayer成为局部变量而不是属性,一切都会好的,但现在声音根本不起作用,我无法弄清楚原因.在我为AVAudioPlayer找到的所有教程中,都创建了一个属性,但没有人解释原因.如果这不起作用,我有什么选择来制作可以重叠的声音?

- (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];
}
Run Code Online (Sandbox Code Playgroud)

NJo*_*nes 8

您需要属性或ivar的原因是它提供的强大参考.当使用ARC时,任何没有强指针的对象都是公平的解除分配游戏,实际上这就是你所看到的.

你也是正确的,AVAudioPlayer强指针一次只允许一个音频播放器被引用.

如果您选择继续使用,解决方案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];
    }
}
Run Code Online (Sandbox Code Playgroud)

通常,AVAudioPlayer对于声音效果/音板应用来说是过度的.对于快速声音"丢弃",您可能会找到音频工具箱框架,如我对此问题的回答中所述.

从查看System Sound类参考,您似乎一次只能播放一个声音.

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

什么是能够播放可重叠的声音的最佳方式,同时仍然有效的代码和内存量?

最好的是意见.

如果您需要同时播放两个相同声音的实例,那么我会说这个答案中发布的代码将是要使用的代码.由于相同声音的每个重叠实例都需要创建新资源,因此使用它的代码audioPlayerDidFinishPlaying:更易于管理(内存可以轻松回收).

如果相同声音的重叠实例不是交易破坏者,那么我认为仅使用AudioServicesCreateSystemSoundID()创建每个声音的一个实例更有效.

SystemSoundID每按一次按钮,我绝对不会尝试管理s 的创建和处理.那会匆忙出错.在那种情况下AVAudioPlayer,仅仅是可维护性就是明显的赢家.


Ste*_*vin 5

我假设你正在使用ARC.音频播放器不起作用的原因是因为AVAudioPlayer对象正在被释放,然后一旦loadSound:方法终止就被销毁.由于ARC的对象管理,这种情况正在发生.在ARC之前,代码:

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

会像预期的那样播放声音.但是,该方法AVAudioPlayer在loadSound:方法终止后很久仍会存在.这意味着每次播放声音时,都会泄漏记忆.

关于属性的一点点

引入属性是为了减少开发人员必须编写和维护的代码量.在属性之前,开发人员必须手工为每个实例变量编写setter和getter.这是很多冗余的代码.属性的一个幸运副作用是它们处理了为基于对象的实例变量编写setter/getter所需的大量内存管理代码.这意味着许多开发人员开始专门使用属性,即使对于不需要公开的变量也是如此.

由于ARC会为您处理所有内存管理细节,因此属性应仅用于其原始用途,从而减少冗余代码的数量.默认情况下,传统的iVars将被强烈引用,这意味着一个简单的赋值,例如: title = [NSString stringWithFormat:@"hello"]; 与代码基本相同: self.title = [NSString stringWithFormat:@"hello"];.

好的,回到你的问题.

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