Вот контекст: Я разрабатываю приложение, связанное с аудио, какое-то время, и я как бы ударил стену и не уверен, что делать дальше.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];
});
Что мой вопрос:
- Я получаю различные проблемы с потоками, особенно в основном потоке, таком как
Thread 1: EXC_BAD_ACCESS (code=1, address=0xf00000c)
, иногда при запуске приложения, когда вызывается viewDidLoad, но также, когда я пытаюсь взаимодействовать w с любым объектом пользовательского интерфейса. - Чувствительность пользовательского интерфейса становится безумно медленной, даже на дисплее FFT.
То, что я думаю, что проблема есть: Это, безусловно, связано с вопросом заправочной, как вы знаете, но я действительно неопытный с этой темой. Я подумал о том, что может заставить любое обновление отображения пользовательского интерфейса в основном потоке решить проблемы, которые у меня есть, но снова; Я даже не знаю, как это сделать.
Любой вход/проницательность будет огромной помощью. Спасибо заранее!
Что нить выше код работает на? _I думал о том, чтобы заставить любое обновление дисплея пользовательского интерфейса в основном потоке, чтобы решить проблемы, которые у меня есть, но снова; Я даже не знаю, как это сделать правильно. Все вызовы UIKit должны встречаться в основном потоке, и вы можете сделать это из другого потока, вызвав 'dispatch_async()' с основной очередью, как вы это делаете в последние строки кода, выше. Вы не должны вызывать вызовы UIKit из очереди 'dispatch_get_global_queue()' (для параллельного выполнения). – Mark
Вы уверены, что ваш синглтон SpectralView' является потокобезопасным? Метод 'sharedInstance' должен использовать' dispatch_once', и убедитесь, что методы, которые вы вызываете на него из фона, никоим образом не обновляют пользовательский интерфейс. Вы также должны убедиться, что любые свойства на нем, вызывающие из нескольких потоков, правильно сериализуются. – Hamish
@ оригиналuser2 действительно хороший пункт. Я даже не заметил, что я не использовал 'dispatch_once' в моем представлении. –