从 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
会有所不同?
所以现在我有三个问题:
- "Jo" 日志之间的那一秒发生了什么?
- 为什么
dispatch_async
有所作为?
- 在生产中使用这种方法是不好的做法吗?
非常感谢!
编辑:
还有一件事我不明白。
当我使用 [self setNeedsDisplay];
方法时,主队列上的所有其他调用似乎都处于饥饿状态。触摸事件不再触发,并且 RestKit 的回调永远不会被传递。 [self setNeedsDisplay];
是否以某种方式没有添加到队列的末尾,而是添加到队列的开头?
在那一秒钟内,很可能没有发生任何事情,因为没有任何事情触发重绘。这个管道相当复杂,但是 setNeedsDisplay
等方法在主线程上调用时会做更多的工作,因为它会通知 window 层次结构它发生了变化,并将重绘需要重绘的元素.此管道很可能连接到主 运行 循环,该循环只能从主线程访问。
所以当您从其他线程调用它时,您实际上标记了它需要重绘的视图,但您没有通知 运行 循环实际触发重绘过程。
所以:
- 真的没什么特别的。它只是在等待。
- 它在触发刷新管道时产生了不同。
- 完全不是一个坏习惯,但是如果稍后会有更多元素调用它,请注意如何执行此过程。
对于 good/bad 实践视情况而定,但我会做的是创建一个包含显示 link 的 class。我会添加 2 个方法,例如 retainAnimation
和 releaseAnimation
。这 2 将增加或减少整数值 retainAnimationCount
然后覆盖其 setter 以便:
- 如果计数从零开始增加显示 link 开始
- 如果计数减少到零,则显示 link 停止
显示 link 将调用委托、给定块或简单地硬编码调用 setNeedsDisplay
给定视图。 class 本身可以将输出调用恢复到主线程,但可以从任何线程调用释放和保留调用。
编辑:
这个 class 在您的案例中是如何使用的 您将在动画开始后调用 retain 方法,在动画结束后调用 release 方法。所有其余的应该已经在 class 本身中处理了。好处主要是如果你有多个 "animation" 对象,将不会额外调用刷新方法,交错没有问题,何时开始或何时停止,你从哪个线程调用它......但是做确保您没有将保留计数 属性 标记为 nonatomic
,因为您仍应保持线程安全。
我正在使用按需渲染构建自定义 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
会有所不同?
所以现在我有三个问题:
- "Jo" 日志之间的那一秒发生了什么?
- 为什么
dispatch_async
有所作为? - 在生产中使用这种方法是不好的做法吗?
非常感谢!
编辑:
还有一件事我不明白。
当我使用 [self setNeedsDisplay];
方法时,主队列上的所有其他调用似乎都处于饥饿状态。触摸事件不再触发,并且 RestKit 的回调永远不会被传递。 [self setNeedsDisplay];
是否以某种方式没有添加到队列的末尾,而是添加到队列的开头?
在那一秒钟内,很可能没有发生任何事情,因为没有任何事情触发重绘。这个管道相当复杂,但是 setNeedsDisplay
等方法在主线程上调用时会做更多的工作,因为它会通知 window 层次结构它发生了变化,并将重绘需要重绘的元素.此管道很可能连接到主 运行 循环,该循环只能从主线程访问。
所以当您从其他线程调用它时,您实际上标记了它需要重绘的视图,但您没有通知 运行 循环实际触发重绘过程。
所以:
- 真的没什么特别的。它只是在等待。
- 它在触发刷新管道时产生了不同。
- 完全不是一个坏习惯,但是如果稍后会有更多元素调用它,请注意如何执行此过程。
对于 good/bad 实践视情况而定,但我会做的是创建一个包含显示 link 的 class。我会添加 2 个方法,例如 retainAnimation
和 releaseAnimation
。这 2 将增加或减少整数值 retainAnimationCount
然后覆盖其 setter 以便:
- 如果计数从零开始增加显示 link 开始
- 如果计数减少到零,则显示 link 停止
显示 link 将调用委托、给定块或简单地硬编码调用 setNeedsDisplay
给定视图。 class 本身可以将输出调用恢复到主线程,但可以从任何线程调用释放和保留调用。
编辑:
这个 class 在您的案例中是如何使用的 您将在动画开始后调用 retain 方法,在动画结束后调用 release 方法。所有其余的应该已经在 class 本身中处理了。好处主要是如果你有多个 "animation" 对象,将不会额外调用刷新方法,交错没有问题,何时开始或何时停止,你从哪个线程调用它......但是做确保您没有将保留计数 属性 标记为 nonatomic
,因为您仍应保持线程安全。