Вызов setNeedsDisplay из GLKView drawRect с / без dispatch_async
Я строю обычай GLKView
используя рендеринг по требованию. В большинстве случаев представление будет перерисовываться только на сенсорных событиях (и это работает), но иногда бывают короткие анимации, где я хотел бы перерисовать в цикле.
Моя первая попытка выглядела так:
-(void)drawRect:(CGRect)rect {
NSLog(@"Jo");
glClearColor(1, 0, 0, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
[self setNeedsDisplay];
}
Мое понимание исходит от Android
было то, что это должно продолжать очищать экран как можно быстрее и регистрировать много "Джо". Что на самом деле происходит, так это то, что "Джо" регистрируется примерно раз в секунду, представление никогда не очищается вообще, но CPU
использование остается 0.
Если я изменю
[self setNeedsDisplay];
в
dispatch_async(dispatch_get_main_queue(), ^{
[self setNeedsDisplay];
});
все работает точно так, как ожидалось.
Насколько я понимаю drawRect
в любом случае вызывается из основного потока, так почему же dispatch_async
Сделать разницу?
Итак, теперь у меня есть три вопроса:
- Что происходит за одну секунду между журналами "Джо"?
- Почему
dispatch_async
различать? - Это плохая практика, чтобы использовать этот подход в производстве?
Большое спасибо!
РЕДАКТИРОВАТЬ:
Есть еще одна вещь, которую я не понимаю.
Когда я использую [self setNeedsDisplay];
метод, все остальные вызовы в главной очереди, кажется, голодать. События касания больше не запускаются, и обратный вызов от RestKit никогда не доставляется. Есть ли [self setNeedsDisplay];
как-то не добавить в конец очереди, но в начале?
1 ответ
В эту секунду, скорее всего, ничего не происходит, поскольку ничего не вызвало перерисовку. Этот конвейер довольно сложный, но такие методы, как setNeedsDisplay
при вызове в главном потоке будет немного больше работать, поскольку он уведомит оконную иерархию об изменениях и перерисовает элементы, необходимые для перерисовки. Этот конвейер, скорее всего, подключен к основному циклу выполнения, доступ к которому возможен только из основного потока.
Поэтому, когда вы вызываете его из какого-то другого потока, вы на самом деле помечаете представление, которое нужно перерисовать, но не уведомляете цикл выполнения для фактического запуска процедуры перерисовки.
Так:
- Ничего особенного на самом деле. Это просто ожидание.
- Это имеет значение, так как запускает конвейер обновления.
- Совсем не плохая практика, но будьте осторожны, как вы выполняете эту процедуру, если позже больше элементов будут вызывать это.
Хорошая / плохая практика зависит от ситуации, но я бы скорее создал класс, содержащий ссылку для отображения. Я бы добавил 2 метода, такие как retainAnimation
а также releaseAnimation
, Эти 2 будут увеличивать или уменьшать целочисленное значение retainAnimationCount
затем переопределите его установщик так, чтобы:
- Если счет увеличивается с нуля, ссылка на дисплей запускается
- Если количество уменьшается до нуля, ссылка на дисплей останавливается
Ссылка на отображение будет либо вызывать делегата, данный блок, либо просто жестко закодированный вызов setNeedsDisplay
для данного взгляда. Сам класс может возвращать исходящий вызов в основной поток, но вызовы освобождения и сохранения могут вызываться из любого потока.
РЕДАКТИРОВАТЬ:
Как этот класс используется в вашем случае, вы будете вызывать метод retain, как только ваша анимация начнется, и выпустить метод, как только он закончит анимацию. Все остальное уже должно быть обработано в самом классе. Преимущества, в основном, заключаются в том, что если у вас есть несколько "анимационных" объектов, не будет никаких дополнительных вызовов метода refresh, нет проблем с перемежением, когда начинать или когда останавливать, из какого потока вы вызываете его... Но убедитесь, что Вы не помечаете свойство сохранения количества как nonatomic
как вы все еще должны держать его в безопасности.