从 GLKView drawRect 调用 setNeedsDisplay with/without dispatch_async

Calling setNeedsDisplay from GLKView drawRect with/without 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 的理解是,这应该尽可能快地清除屏幕并记录大量 "Jo"。 实际发生的情况是 "Jo" 大约每秒记录一次,视图根本不会被清除,但 CPU 使用仍然为 0。

如果我改变

[self setNeedsDisplay];

dispatch_async(dispatch_get_main_queue(), ^{
    [self setNeedsDisplay];
});

一切都按预期工作。

据我了解 drawRect 无论如何都是从主线程调用的,那么为什么 dispatch_async 会有所不同?

所以现在我有三个问题:

  1. "Jo" 日志之间的那一秒发生了什么?
  2. 为什么 dispatch_async 有所作为?
  3. 在生产中使用这种方法是不好的做法吗?

非常感谢!

编辑:

还有一件事我不明白。

当我使用 [self setNeedsDisplay]; 方法时,主队列上的所有其他调用似乎都处于饥饿状态。触摸事件不再触发,并且 RestKit 的回调永远不会被传递。 [self setNeedsDisplay]; 是否以某种方式没有添加到队列的末尾,而是添加到队列的开头?

在那一秒钟内,很可能没有发生任何事情,因为没有任何事情触发重绘。这个管道相当复杂,但是 setNeedsDisplay 等方法在主线程上调用时会做更多的工作,因为它会通知 window 层次结构它发生了变化,并将重绘需要重绘的元素.此管道很可能连接到主 运行 循环,该循环只能从主线程访问。

所以当您从其他线程调用它时,您实际上标记了它需要重绘的视图,但您没有通知 运行 循环实际触发重绘过程。

所以:

  1. 真的没什么特别的。它只是在等待。
  2. 它在触发刷新管道时产生了不同。
  3. 完全不是一个坏习惯,但是如果稍后会有更多元素调用它,请注意如何执行此过程。

对于 good/bad 实践视情况而定,但我会做的是创建一个包含显示 link 的 class。我会添加 2 个方法,例如 retainAnimationreleaseAnimation。这 2 将增加或减少整数值 retainAnimationCount 然后覆盖其 setter 以便:

  1. 如果计数从零开始增加显示 link 开始
  2. 如果计数减少到零,则显示 link 停止

显示 link 将调用委托、给定块或简单地硬编码调用 setNeedsDisplay 给定视图。 class 本身可以将输出调用恢复到主线程,但可以从任何线程调用释放和保留调用。

编辑:

这个 class 在您的案例中是如何使用的 您将在动画开始后调用 retain 方法,在动画结束后调用 release 方法。所有其余的应该已经在 class 本身中处理了。好处主要是如果你有多个 "animation" 对象,将不会额外调用刷新方法,交错没有问题,何时开始或何时停止,你从哪个线程调用它......但是做确保您没有将保留计数 属性 标记为 nonatomic,因为您仍应保持线程安全。