Что заставляет CADisplayLink запускаться только время от времени / задерживаться?
Может быть, вам не нужна вся эта информация, чтобы помочь мне с этой проблемой. Основные вопросы: что может вызвать CADisplayLink
отложить стрельбу, и как проверить на возможные причины?
Я использую CADisplayLink
таймер скорости / прокрутки по инерции - моя собственная реализация представления формы звукового сигнала:
- (void) startVelocityScrollTimer {
dispatch_sync(dispatch_get_main_queue(), ^{
if (_velocityScrollTimer == nil) {
_velocityScrollTimer = [CADisplayLink displayLinkWithTarget:self selector:@selector(velocityScroll)];
_velocityScrollTimer.frameInterval = 1;
[_velocityScrollTimer addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}
});
}
У меня проблемы с точностью таймера. Обычно он срабатывает 50 или 60 раз в секунду (не уверен, но здесь точное значение не имеет значения). Во время определенных действий эта скорость снижается до 3–5 пожаров в секунду, что слишком мало для плавной прокрутки. Я не могу определить причину этих задержек.
Я пробовал:
- Полностью отсоединение чертежа и загрузки с помощью аутсорсинга загрузки аудиоданных в выделенную очередь отправки, которая загружает данные асинхронно.
- Профилирование и оптимизация метода рисования. На пиках это занимает максимум 6 мс.
- Профилирование метода, вызываемого при срабатывании таймера (speedScroll:). Выполнение обычно занимает менее 1 мс, иногда пики достигают 3 мс.
- с помощью
NSRunLoopCommonModes
вместоNSDefaultRunLoopMode
дляCADisplayLink
таймер
В кешировании у меня много dispatch_sync и несколько dispatch_barrier_sync. Хотя большинство из них не имеет значения, я
- профилировать каждую отправку время начала в моем кэшировании
- профилируется каждый раз выполнения отправленных блоков в кешировании
Хотя большинство из них должны быть неактуальными. Я не нашел ожиданий / блокировок, которые занимали более 50 мс. Это немного медленно, но все же я думаю, что таймер не должен опускаться до 5 срабатываний в секунду или хуже.
Для профилирования я не использовал инструмент профилирования (не мог их обработать). Вместо этого я использовал типичный подход:
double start = CACurrentMediaTime();
// code to profile here
NSLog(@"it took %.2fms", (CACurrentMediaTime() - start) * 1000);
Вот метод, выполняемый при срабатывании таймера. Три метода в середине - это в основном простые простые вычисления позиции, которые выполняются практически мгновенно, за исключением последнего, который выполняет dispatch_sync для главной очереди. Тем не менее, это завершается очень быстро (менее 3 мс, см. Выше).
- (void) velocityScroll {
Float32 cyclesPerSecond = 1.0 / _velocityScrollTimer.duration;
[self applyScrollVelocityToScrollPosition:cyclesPerSecond];
[self applyScrollVelocityDecelerationToScrollVelocity:cyclesPerSecond];
[self ensureVelocityScrollOnlyActiveIfNeeded];
[self setNeedsDisplay];
}
Я знаю, что вы, ребята, хотели бы видеть больше кода, но кода много, и я не хочу публиковать его все. Пожалуйста, попросите отдельные детали, которые я опубликую здесь, если они вам нужны.
Что может вызвать задержки между пожарами CADisplayLink
таймер или как это узнать?