【问题标题】:AVAudioPlayer stops playing immediately with ARCAVAudioPlayer 立即停止播放 ARC
【发布时间】:2011-12-03 07:40:24
【问题描述】:

我正在尝试通过AVAudioPlayer 播放 MP3,我认为这很简单。不幸的是,它并不完全有效。这就是我所做的一切:

  • 为了测试,我新建了一个iOS应用(Single 在 Xcode 中查看)。
  • 我将 AVFoundation 框架添加到项目中,并将 #import <AVFoundation/AVFoundation.h> 添加到 ViewController.m

  • 我将一个 MP3 文件添加到 Apps 的“Documents”文件夹中。

  • 我将ViewControllers viewDidLoad: 更改为以下内容:

代码:

- (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];
}

不幸的是,音频显然在开始播放后立即停止。如果我取消注释 sleepForTimeInterval 它会播放 20 秒然后停止。只有在使用 ARC 编译时才会出现此问题,否则,它可以正常工作。

【问题讨论】:

  • 您不需要启动另一个线程来播放音频。您是否尝试过提供错误指针以查看是否记录了任何内容?
  • 是的,我向 initWithContensOfURL 传递了一个 NSError。不过它保持为零......所以我没有收到任何错误,它只是立即停止播放。
  • 你用ARC编译吗?接缝是我能想到的唯一合理的方式,这会导致玩家被释放和释放。
  • ARC 不在 NDA 范围内。 (在这里了解到)只是问您如何解决它或问题出在哪里: viewDidLoad 完成后,没有任何内容指向您的 audioPlayer,因此 ARC 将其释放。你可能为它创建了一个 ivar/property。这行得通吗?
  • 是的,这行得通。正确的答案正在路上:)

标签: objective-c ios avfoundation avaudioplayer automatic-ref-counting


【解决方案1】:

问题在于,当使用 ARC 编译时,您需要确保保留对要保持活动状态的实例的引用,因为编译器将通过插入 release 调用(在至少从概念上讲,请阅读Mikes Ash blog post for more details)。您可以通过将实例分配给属性或实例变量来解决此问题。

在 Phlibbo 的情况下,代码将被转换为:

- (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
}

AVAudioPlayer 将立即停止播放,因为当没有留下任何引用时它会被释放。

我自己没有使用过 ARC,只是简单地阅读了一下。如果您对此有更多了解,请评论我的回答,我会更新更多信息。

更多ARC信息:
Transitioning to ARC Release Notes
LLVM Automatic Reference Counting

【讨论】:

  • 我不使用 ARC,我尝试以与您相同的方式播放短音。我只听到第一个声音(这要归功于 AVAudioPlayer 第一次播放声音的错误)。因此,最好的情况是您的代码会剪切音频的前几秒,最坏的情况是它会在声音自行停止之前停止播放。
  • @Gargo:正如 Mattias 在他的帖子中所写,代码解释了为什么播放器在我的情况下无法正常工作,这是故意的错误。如果您没有经验,您应该查看 AVAudioPlayer 教程。
  • @Gargo 如果你包含[audioPlayer release]; // inserted by ARC 行,你很可能会得到一个很短的声音或根本没有声音(我认为 AVAudioPlayer 使用线程?)因为播放器被直接释放。如果你不包括那条线,你会得到声音,但每次都会泄漏一个AVAudioPlayer 实例。另请注意,autorelease 不起作用。您需要等到声音播放完毕(使用委托协议),或者另一种选择是重用同一个播放器。希望对您有所帮助。
【解决方案2】:

在带有strong的头文件中使用AVAudioPlayer作为ivar:

@property (strong,nonatomic) AVAudioPlayer *audioPlayer

【讨论】:

    【解决方案3】:

    如果您需要同时播放多个 AVAudioPlayer,请创建一个 NSMutableDictionary。将密钥设置为文件名。通过委托回调从字典中删除,如下所示:

    -(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]];
    }
    

    【讨论】:

    • 这是同时播放声音的最佳解决方案。如果您使用 AVAudioPlayer 作为属性,那么当您播放下一个声音并且有些人正在使用 AudioServicesPlaySystemSound 但苹果拒绝应用程序时,该对象将释放,因为它仍然会在设备静音并且没有 api 来检查设备是否是静音。
    猜你喜欢
    • 2011-12-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多