【问题标题】:AVAudioPlayer - must you create a property for it to work? (Xcode)AVAudioPlayer - 你必须创建一个属性让它工作吗? (Xcode)
【发布时间】:2012-11-21 02:38:26
【问题描述】:

这是我现在使用的代码,但它不起作用(当我按下调用此方法的按钮时没有任何反应)。以前,我有一个用于 audioPlayer 的属性并且它可以工作(下面的所有 audioPlayer 显然都是 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];
}

【问题讨论】:

    标签: objective-c ios xcode audio avaudioplayer


    【解决方案1】:

    您需要属性或 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];
        }
    }
    

    一般来说,AVAudioPlayer 对于音效/音板应用来说太过分了。对于快速的声音“下降”,您可能会找到音频工具箱框架,如outlined in my answer to this question.

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

    一次只能播放一个SystemSoundID。例如,如果你有 soundOne 和 soundTwo。您可以在 soundTwo 播放时播放 soundOne,但一次不能播放多个声音实例。

    播放可以重叠的声音的最佳方式是什么? 在代码量和内存量上仍然高效?

    最好的是意见。

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

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

    我绝对不会尝试通过每次按下按钮来管理 SystemSoundIDs 的创建和处置。那会很快出错。在那种情况下,AVAudioPlayer 显然是仅在可维护性方面的赢家。

    【讨论】:

    • 从查看 System Sound 类参考,您似乎一次只能播放一种声音。能够播放可以重叠的声音同时仍然有效地利用代码和内存量的最佳方式是什么?
    • 帮助我解决了停止当前正在播放的播放器并在启动另一个播放器时释放它的问题(没有停止播放按钮)。现在内存使用量并没有飙升,只保留了一个音频播放器实例。谢谢
    • 有没有人迅速成功并可以分享sn-p?
    • audioPlayerDecodeErrorDidOccur:error: 方法中删除播放器可能也是个好主意。
    【解决方案2】:

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

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

    会按预期播放声音。但是,AVAudioPlayer 对象在loadSound: 方法终止后很长时间仍将存在。这意味着每次播放声音时,都会发生内存泄漏。

    关于属性的小知识

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

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

    好的,回到你的问题。

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

    【讨论】:

      猜你喜欢
      • 2013-07-28
      • 2011-06-29
      • 2018-01-25
      • 1970-01-01
      • 2019-05-17
      • 2010-09-07
      • 1970-01-01
      • 2019-08-11
      • 2021-07-24
      相关资源
      最近更新 更多