【问题标题】:Calling setNeedsDisplay from GLKView drawRect with/without dispatch_async使用/不使用 dispatch_async 从 GLKView drawRect 调用 setNeedsDisplay
【发布时间】:2015-06-02 19:12:32
【问题描述】:

我正在使用按需渲染构建自定义 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 use 仍然为 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]; 是否不会以某种方式添加到队列的末尾,而是添加到队列的开头?

【问题讨论】:

    标签: ios glkview


    【解决方案1】:

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

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

    所以:

    1. 真的没有什么特别的。它只是在等待。
    2. 它会触发刷新管道。
    3. 这不是一个坏习惯,但要小心如何执行此过程,如果以后有更多元素会调用它。

    对于好/坏的做法取决于具体情况,但我宁愿创建一个包含显示链接的类。我会添加 2 个方法,例如 retainAnimationreleaseAnimation。这两个将增加或减少整数值retainAnimationCount,然后覆盖其设置器,以便:

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

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

    编辑:

    在您的情况下如何使用此类,您将在动画开始后调用 retain 方法,并在动画完成后调用 release 方法。所有其余的应该已经在类本身中处理。好处主要是如果您有多个“动画”对象,则不会对刷新方法进行额外调用,交错没有问题,何时开始或何时停止,您从哪个线程调用它......但请确保您没有将保留计数属性标记为nonatomic,因为您仍应保持线程安全。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多