iOS:在回调函数中调用 setNeedDisplay 时出现线程问题



这是上下文: 我已经开发一个与音频相关的应用程序一段时间了,我有点碰壁,不知道下一步该怎么做。

我最近在应用程序中实现了一个自定义类,该类绘制音频输出的 FFT 显示。此类是 UIView 的一个子类,这意味着每次我需要绘制新的 FFT 更新时,我都需要使用新的示例值在类的实例上调用setNeedDisplay

由于我需要为每一帧(帧 ~= 1024 个样本(绘制一个新的 FFT,这意味着我的 FFT 的显示函数被调用了很多(1024/采样率 ~= 0.02321 秒(。至于样本计算,它是44'100/秒完成的。我在 iOS 中管理线程方面没有真正的经验,所以我读了一些关于它的信息,这是我是如何做到的。

它是如何完成的:我有一个NSObject"AudioEngine.h"的子类,它负责我的应用程序中的所有DSP处理,这是我设置FFT显示的地方。所有样本值都被计算并分配给dispatch_get_global_queue块内的FFT子类,因为这些值需要在后台不断更新。一旦样本索引达到最大帧数,就会调用 setneedDisplay 方法,这是在 dispatch_async(dispatch_get_main_queue) 块内完成

在"音频引擎.m"中

for (k = 0; k < nchnls; k++) {
            buffer = (SInt32 *) ioData->mBuffers[k].mData;
            if (cdata->shouldMute == false) {
                buffer[frame] = (SInt32) lrintf(spout[nsmps++]*coef) ;
                    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
                        @autoreleasepool {
                            // FFT display init here as a singleton
                            SpectralView *specView = [SpectralView sharedInstance];
                            //Here is created a pointer to the "samples" property of my subclass
                            Float32 *specSamps = [specView samples];
                            //set the number of frames the FFT should take
                            [specView setInNumberFrames:inNumberFrames];
                            //scaling sample values
                            specSamps[frame] = (buffer[frame] * (1./coef) * 0.5);
                        }
                    });
            } else {
             // If output is muted
                buffer[frame] = 0;
            }
        }
        //once the number of samples has reached ksmps (vector size) we update the FFT
        if (nsmps == ksmps*nchnls){
                dispatch_async(dispatch_get_main_queue(), ^{
                    SpectralView *specView = [SpectralView sharedInstance];
                    [specView prepareToDraw];
                    [specView setNeedsDisplay];
                });

我的问题是什么:

  1. 我遇到了各种线程问题,尤其是在主线程(例如 Thread 1: EXC_BAD_ACCESS (code=1, address=0xf00000c) (上,有时在调用 viewDidLoad 时在应用程序启动时,但每当我尝试与任何 UI 对象交互时也是如此。
  2. 即使在 FFT 显示器上,UI 响应速度也会变得非常慢。

我认为问题是:正如您可能知道的那样,它肯定与线程问题有关,但我对这个主题真的没有经验。我想过也许在主线程上强制任何 UI 显示更新,以解决我遇到的问题,但又一次;我什至不确定如何正确地做到这一点。

任何输入/见解都将是一个巨大的帮助。提前感谢!

如前所述,您的SpectralView*需要完全线程安全。

您的for()循环首先将帧/样本处理推送到高优先级并发队列。 由于这是异步的,它将立即返回,此时您的代码将对主要威胁的请求进行排队,以更新光谱视图的显示。

这几乎保证了光谱视图必须

同时更新显示,后台处理代码也会更新光谱视图的状态。

还有第二个问题; 您的代码最终将并行处理所有通道。 通常,未缓解的并发性会导致性能降低。 此外,您将在每个通道的主线程上引起更新,无论该通道的处理是否完成。


代码需要重新构建。 您确实应该将模型层与视图层分开。 可以将模型层写入为线程安全,也可以在处理过程中获取要显示的数据的快照并将其扔到SpectralView上。 或者,您的模型图层可以有一个isProcessing标志,SpectralView 可以关闭该标志,以了解它不应该读取数据。

这是相关的:

https://developer.apple.com/library/ios/documentation/General/Conceptual/ConcurrencyProgrammingGuide/Introduction/Introduction.html

最新更新