2016-02-08 4 views
1

Вот контекст: Я разрабатываю приложение, связанное с аудио, какое-то время, и я как бы ударил стену и не уверен, что делать дальше.iOS: проблема с Threading при вызове setNeedDisplay внутри функции обратного вызова

Я недавно внедрил в приложение пользовательский класс, который отображает FFT-дисплей аудиовыхода. Этот класс является подклассом UIView, означающим, что каждый раз, когда мне нужно построить новое обновление FFT, мне нужно вызвать setNeedDisplay в моем экземпляре класса с новыми значениями образца.

Как мне нужно построить новый FFT для каждого кадра (кадр ~ = 1024 выборки), это означает, что функция отображения моего БПФ называется много (1024/SampleRate ~ = 0,02321 секунды). Что касается расчета выборки, то это делается 44'100/сек. Я не очень разбираюсь в управлении потоками в iOS, поэтому я немного читаю об этом, и вот как я это сделал.

Как это было сделано: У меня есть подкласс NSObject «AudioEngine.h», который принимает на себя все обработки DSP в моем приложении, и это где я устанавливаю мой FFT дисплей. Все значения выборки вычисляются и присваиваются моему подклассу FFT внутри блока dispatch_get_global_queue, так как значения должны постоянно обновляться в фоновом режиме. Метод setneedDisplay вызывается один раз индекс выборки достиг максимального количества кадров, и это делается внутри блока dispatch_async(dispatch_get_main_queue)

В «AudioEngine.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, но также, когда я пытаюсь взаимодействовать w с любым объектом пользовательского интерфейса.
  2. Чувствительность пользовательского интерфейса становится безумно медленной, даже на дисплее FFT.

То, что я думаю, что проблема есть: Это, безусловно, связано с вопросом заправочной, как вы знаете, но я действительно неопытный с этой темой. Я подумал о том, что может заставить любое обновление отображения пользовательского интерфейса в основном потоке решить проблемы, которые у меня есть, но снова; Я даже не знаю, как это сделать.

Любой вход/проницательность будет огромной помощью. Спасибо заранее!

+1

Что нить выше код работает на? _I думал о том, чтобы заставить любое обновление дисплея пользовательского интерфейса в основном потоке, чтобы решить проблемы, которые у меня есть, но снова; Я даже не знаю, как это сделать правильно. Все вызовы UIKit должны встречаться в основном потоке, и вы можете сделать это из другого потока, вызвав 'dispatch_async()' с основной очередью, как вы это делаете в последние строки кода, выше. Вы не должны вызывать вызовы UIKit из очереди 'dispatch_get_global_queue()' (для параллельного выполнения). – Mark

+0

Вы уверены, что ваш синглтон SpectralView' является потокобезопасным? Метод 'sharedInstance' должен использовать' dispatch_once', и убедитесь, что методы, которые вы вызываете на него из фона, никоим образом не обновляют пользовательский интерфейс. Вы также должны убедиться, что любые свойства на нем, вызывающие из нескольких потоков, правильно сериализуются. – Hamish

+0

@ оригиналuser2 действительно хороший пункт. Я даже не заметил, что я не использовал 'dispatch_once' в моем представлении. –

ответ

2

Как указано, ваш SpectralView* должен быть полностью потокобезопасным.

Ваш цикл for() - это первая передача кадров/обработка образца в очередь с приоритетным приоритетом. Поскольку это асинхронно, оно немедленно вернется, и в этот момент ваш код будет помещать запрос на главную угрозу для обновления отображения спектрального представления.

Это в значительной степени гарантирует, что спектральный вид должен будет обновлять дисплей одновременно с кодом обработки фона, также обновляя состояние спектрального представления.

Существует второй вопрос; ваш код будет заканчиваться распараллеливанием обработки всех каналов. В общем, unmitigated concurrency - это рецепт медленной производительности.Кроме того, вы будете вызывать обновление основного потока для каждого канала, независимо от того, завершена ли обработка этого канала.


Код необходимо изменить. Вы действительно должны разделить слой модели на уровне представления. Слой модели может быть записан как потокобезопасный или, во время обработки вы можете получить снимок данных для отображения и бросить их в SpectralView. В качестве альтернативы ваш модельный слой может иметь флаг isProcessing, который SpectralView мог бы отключить, чтобы знать, что он не должен читать данные.

Это актуально:

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

+1

Хорошее резюме проблем. К OP: хорошее эмпирическое правило: если у вас есть объект, являющийся подклассом UIView, его нельзя трогать из фонового потока. –

+0

Большое спасибо за этот ответ и действительно полезный ресурс. Похоже, мне нужно сделать некоторые чтения сейчас! –