1

我试图了解有关并发编程和调用 setNeedsDisplay 的工作原理。我基本上有三个对象。

Main View - container with different UIView objects, the main one being a UIScrollView
Small Map View - a small UIView that draws a miniature version of one of the other UIView items on screem
Processor - a delegate of the Main View that calculates what's on screen and calls the Main View back with what's in view.  

因此,一个简单的用例是用户触摸 ScrollView,然后处理器更新 scrollView 的视图(如计算坐标、中心点等)它使用块并异步执行此操作。然后,这会将通知发布到 MainView 对象。

当 MainView 收到通知时,它只是调用

[smallMap setNeedsDisplay];  // example 1

我在这个电话周围放了一些日志,我确实看到它立即被调用。但是,此函数的 drawRect: 不会立即被调用。它在 2 秒左右后被调用。

我记得读过这setNeedsDisplay只是标记视图以在运行循环的下一个事件上发生重绘。

但是,如果我添加此代码:

// example 2
dispatch_async(dispatch_get_main_queue(), ^{
    [smallMap setNeedsDisplay];        
    });

我的视图立即被重绘。

我想我很困惑为什么我必须要求主事件循环调用 setNeedsDisplay 来立即重绘某些东西。就像在示例 1 中一样,我调用 setNeedsDisplay,是在后台完成的还是其他什么,这就是为什么它没有立即重绘的原因?我试图了解幕后发生的事情的不同之处,以便我知道将来要寻找什么。就像我是否应该在类似于示例 2 块的内容中立即重绘所有调用?或者是因为我正在异步处理我的数据,然后我需要请求主队列?谢谢!

4

2 回答 2

2

setNeedsDisplay是 UIKIT API 调用,必须从应用程序的主线程调用,也称为 UI 线程。这就是为什么在后台线程中调用它不会立即产生任何效果,而在主队列上调度它会立即产生效果。

有关更详细的答案,请参阅此相关问题https://stackoverflow.com/a/6988115/172690 。

于 2012-06-04T21:12:46.700 回答
2

我的猜测是两件事中的一件:

您在单独线程上运行的代码正在从单独的线程调用 MainView 方法,而不是使用 performSelectorOnMainThread 或调用主线程上的代码的 GCD 调用。因此,您对 setNeedsDisplay 的调用实际上是在后台线程上进行的,正如另一位海报所说,这是一个禁忌。

第二种可能性是您的 MainView 代码正在主线程上运行,但它忙于进行耗时的处理,或者等待对另一个线程的同步调用完成,并且不为事件循环提供服务。

您可以通过在对 setNeedsDisplay 的调用上设置断点并查看调试器中的调用跟踪以查看它从哪个线程运行来排除第一种可能性。

找出第二种可能性需要更多的工作。您可能需要深入研究乐器。

于 2012-06-04T23:51:18.583 回答