离屏UIView线程安全

时间:2014-10-12 21:07:18

标签: ios multithreading uikit grand-central-dispatch

在我的应用程序中,我在后台线程上渲染批次(200到400)UIImage,然后通过将UI更新块分派给主线程将它们安装在屏幕上的UIImageView实例中。

一些代码大致显示我在做什么......

dispatch_async( redrawQueue, ^{
  // An array to stuff images in to for the views that have one.
  NSMutableArray *const images = [NSMutableArray arrayWithCount: [activeViews count] value: [NSNull null]];

  for(NSUInteger i=0; i<activeCount; ++i)
  {
    // Rendered content comes from a block in myState.
    UIImage *const image = contentBlock(i);
    if(image)
    {
      images[i] = image;
    }
  }
  else
  {
    return;
  }

  // Update the UI now…
  dispatch_async( dispatch_get_main_queue(), ^{
    [images enumerateObjectsUsingBlock:^(UIImage *image, NSUInteger i, BOOL *stop) {
      UIImageView *const view = [activeViews objectAtIndex:i];
      [view setImage: [NSNull isNotNull: image] ? image : nil];
      layoutBlock(view, i);
    }];
  });
});

这很好用,但在快速滚动期间我仍然会丢帧。看起来这种情况正在发生,因为在视图中设置图像的工作压倒了主线程。我的证据是,如果我只取出代码来实际设置视图中的渲染图像,滚动就会更顺畅。

我想知道解决这个问题的方法是否也可以在后台线程中创建视图,为它们分配图像,并将它们放入容器视图中。然后在主线程中,我只需要将容器交换到屏幕上的场景。结果有点像双缓冲图形上下文,我想 - 更新一个而另一个显示。

任何人都可以建议这不太可能是线程安全的吗?

我做了一个小小的测试,在背景线程上分配屏幕UIViews并将它们嵌套在彼此之内。它还没有崩溃:-)“它还没有崩溃”虽然不是一个伟大的“线程安全”保证!它也没有说明未来版本的iOS可能会发生什么。


显而易见的答案是“嘿,你这个傻瓜,你为什么要使用数百个小视图?将它们合成一个大图像,并将一个视图交换进来。”不幸的是,我需要很多小小的观点,因为我需要独立地移动各个小块。

另一个答案可能是“使用sprite kit,dude”,你可能是对的,但是小视图具有动态大小和内容,我不确定精灵套件的最佳状态有很多精灵更新发生。

第三种方法可能是限制主线程上的UI更新以防止帧被丢弃。有没有这样做的机制?某种调度队列由主线程运行,只有在剩下足够的时间时调用东西?

1 个答案:

答案 0 :(得分:2)

你问:

  

第三种方法可能是限制主要的UI更新   线程以防止帧丢失。有机制吗?   做这个?某种调度队列由主线程运行   只有在剩下足够的时间的时候才会打电话?

这不是内置的东西,但想象你如何做到这一点并不难,但它也是非平凡的。您可能需要管理自己的图像阵列(包括保护它免受并发访问的一些方法),然后添加CFRunLoopObserver(可能在kCFRunLoopBeforeWaiting活动中,因为那是'{1}}当运行循环即将进入休眠状态时,每次触发时,标记开始时间,然后处理图像数组中的项目,直到经过一段时间(10ms可能是一个不错的时间)时间)。

您可能会考虑的另一件事是将许多这些小图片渲染成一个CGImage(或一些少量图像),然后将视图的图层内容设置为大图像image,同时设置边界,使每个实例被剪裁到与该视图对应的部分。这可能会减少GPU纹理上传的次数(从而减少总体开销),因为支持视图的所有CALayers将具有与其内容相同的图像。这可能是我的第一站。