我试图了解并发编程和调用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块的东西?或者是因为我正在异步处理我的数据,然后我需要请求主队列?谢谢!
答案 0 :(得分:2)
setNeedsDisplay
是一个UIKIT API调用,必须从应用程序的主线程调用,也称为UI线程。这就是为什么在后台线程中调用它没有任何直接影响,并且在主队列上调度它会产生直接影响。
有关更详细的答案,请参阅此相关问题https://stackoverflow.com/a/6988115/172690。
答案 1 :(得分:2)
我的猜测是两件事之一:
在单独的线程上运行的代码是从单独的线程调用MainView方法,而不是使用performSelectorOnMainThread或调用主线程上的代码的GCD调用。因此,您对setNeedsDisplay的调用实际上是在后台线程上进行的,这是一个禁忌,正如另一张海报所说的那样。
第二种可能性是您的MainView代码在主线程上运行,但是它忙于执行耗时的处理,或者等待同步调用另一个线程完成,并且不为事件循环提供服务。 / p>
您可以通过在调用setNeedsDisplay时设置断点并查看调试器中的调用跟踪以查看它正在运行的线程来排除第一种可能性。
找出第二种可能性需要更多的工作。您可能需要深入研究仪器。