iOS:在后台加载声音时出现多线程问题

时间:2012-08-13 09:08:02

标签: objective-c ios concurrency avaudioplayer objective-c-blocks

我继承了一些需要整理的代码。该应用程序有一些核心声音,目前,有很多AVAudioPlayer个实例连接到各种ViewController,它们都播放相同的几个声音。

作为重构练习的一部分,我决定实现一个名为SoundController的单例类。对于每个需要播放的声音,此类将包含一个AVAudioPlayer,而不是每个ViewController实例化它们自己,它们可以轻松地使用它们:

[[SoundController controller].majorFunctionSound playAtTime:0];

另一个重要的事情是确保在使用之前调用所有声音的prepareToPlay:以最小化任何延迟。由于只有少数声音,并且它们都可能在任何用户会话期间使用,因此在首次实例化SoundController时预加载所有声音(在后台线程上)是有意义的。在init方法中我有这样的东西:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, nil, ^(void)          
{
NSURL *soundURL = [NSURL fileURLWithPath:[NSString stringWithFormat:@"%@/MAJOR FUNCTION.m4a", [[NSBundle mainBundle] resourcePath]]];
_majorAudioSound = [[AVAudioPlayer alloc] initWithContentsOfURL:soundURL error:nil];
[_majorAudioSound  prepareToPlay];
});

majorAudioSound是(readonly, strong)@synthesize majorAudioSound = _majorAudioSound

我担心的是,在并发性方面,以及我可以采取哪些措施来改进代码的能力有多差(或很好)。具体来说,如果我这样做:

[[SoundController controller].majorFunctionSound playAtTime:0];

很明显,majorFunctionSound可能无法正确启动,具体取决于后台初始化块是否已完成。属性返回nil并且声音根本不起作用会发生更糟糕的情况吗?

可能还有哪些其他问题?有没有办法始终确保AVAudioPlayer已正确设置?

1 个答案:

答案 0 :(得分:1)

首先,我想让你再想一想你的班级是否必须是一个单身人士只是因为你打算只有一个实例。这当然是一种方法,但我认为你的初始化问题继承了你决定使用单例类的事实。

假设您有一个名为SoundManager的类,并且您已将其设为Singleton类。

当您在应用程序中的任何位置请求SoundManager的实例时,您将需要假设已返回的实例已准备好立即使用。如果您在SoundManager中有一个异步的init方法,那么您确实遇到了设计问题,因为如果它是第一次初始化,您应该永远不必知道何时请求Singleton。

由于SoundManager需要初始化,我会让我的应用程序在某种基类中有一个SoundManager实例来处理应用程序流,而不是让它成为Singleton。您可以让您的AppDelegate实例化唯一的SoundManager,或者您可以拥有一个名为ApplicationController的类,或者在初始化时加载应用程序所需的所有内容。然后,您可以通过传递引用或让ApplicationController成为单例来通过此控制器类访问SoundManager实例。当然,如果SoundManager是单例,这也有效,只要你确保在启动时初始化它,但我更喜欢尽可能少的单例类。

现在问你是否知道你的声音是否已加载。

我建议您在让用户开始使用该应用之前加载所有声音。同时,您可以向用户显示某些内容,例如加载屏幕,进度条以及播放声音/音乐(如果您愿意)。以下是结构示例:

  • 创建一个名为SoundManager的类,其属性为“loaded”,从开始时为false
  • 创建一个名为ApplicationController的类,它实例化SoundManager,以及您可能拥有的其他有用类,如TextureManager或LocationManager等。
  • 当应用程序启动时,实例化ApplicationController,然后再实例化SoundManager。
  • 显示加载屏幕
  • 让SoundManager首先加载“加载声音”,加载后再开始播放
  • 完成声音加载后,将“loaded”属性设置为true
  • 当ApplicationController加载了所有内容后,让用户通过淡出加载屏幕开始使用该应用程序。

如果您需要用户在加载声音之前开始使用应用程序,那么您仍然可以通过使用名为“loaded”的属性来使用相同的方法。请记住保持此属性的处理同步。

相关问题