在我的代码中,我将NSView
子类化,并在其drawRect
方法中生成三个线程来执行绘图。
-(void)drawRect:(NSRect)dirtyRect
{
[[self window] setAllowsConcurrentViewDrawing:YES];
[self setCanDrawConcurrently:YES];
[NSThread detachNewThreadSelector:@selector(DrawText) toTarget:self withObject:nil];
[NSThread detachNewThreadSelector:@selector(DrawRectangle) toTarget:self withObject:nil];
[NSThread detachNewThreadSelector:@selector(DrawGradient) toTarget:self withObject:nil];
//Wherease these functions DrawText, DrawRectangle and DrawGradient performs their task as suggested by name.
//In DrawText, DrawRectangle, and DrawGradient lockFocus and unlockFocus is being
// used for multithreaded drawing.
}
当我从Xcode运行相同的程序时,运行正常。输出如下所示。
但是当我从外面运行它时,有问题,输出如下所示。
首先,我想知道从次要线程中绘制是否正确?或者从辅助线程中提取的另一种方式是什么?
这个问题背后的原因是什么?
答案 0 :(得分:7)
Ken Aspeslagh对于从辅助线程中绘制有些不正确(他是正确的,这通常是一个坏主意)。从我所看到的代码中你没有一个很好的用例来绘制辅助线程。你能解释一下你为什么要这样做吗?
您自己已经发现setCanDrawConcurrently:
明确谈到从后台线程调用drawRect:
。请注意,视图窗口必须将allowsConcurrentViewDrawing
设置为YES才能使其生效(这是默认设置)。
苹果自己的Cocoa Drawing Guide有一个关于从辅助线程绘图的部分。我已经强调了一些我认为与你相关的部分。
Application Kit为每个窗口和线程组合维护一个独特的图形上下文。因为每个线程都有自己的给定窗口的图形上下文对象,所以可以使用辅助线程绘制到该窗口。但是有一些警告。
在Windows的正常更新周期中,所有绘图请求都会发送到应用程序的主线程进行处理。当用户事件触发用户界面更改时,会发生正常更新周期。在这种情况下,您可以从应用程序的主线程调用setNeedsDisplay:或setNeedsDisplayInRect:方法(或显示系列方法),以使视图中需要重绘的部分无效。 您不应该从任何辅助线程调用这些方法。
如果要从辅助线程更新窗口或视图,则必须手动将焦点锁定在窗口或视图上并自行启动绘图。锁定焦点为该窗口的图形上下文配置绘图环境。锁定后,您可以配置绘图环境,照常发出绘图命令,然后将图形上下文的内容刷新到窗口缓冲区。
为了在辅助线程上定期绘制,您必须自己通知线程。发送常规通知的最简单方法是使用NSTimer或NSAnimation对象。有关如何为内容设置动画的详细信息,请参阅“高级绘图技术”。
The Cocoa Threading Programming Guide也说了这个:
如果要使用线程绘制视图,请将所有绘图代码括在NSView的lockFocusIfCanDraw和unlockFocus方法之间
除此之外,GCD块调用可能是一种比NSThread
更好的在后台执行小型操作的方法。
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
// you can put each of these calls in their own queue if you want
[self DrawText];
[self DrawRectangle];
[self DrawGradient];
});
然而,这可能与您的问题无关;我之所以提到它只是因为我认为它会更好地帮助你使用GCD队列。
答案 1 :(得分:2)
您应该只从主线程中绘制到屏幕。
编辑:显然非常复杂,所以最好从主线程中抽取到屏幕。 ;)
如果你需要渲染一些花费太多时间来避免阻塞主线程的东西,可以考虑使用线程来绘制到屏幕外的上下文,然后将该上下文复制到主线程的屏幕上。
答案 2 :(得分:1)
我在Thread guide了解了NSGraphicsContext Restriction
。
在这里,我找到了以下一行:
如果从辅助线程执行任何绘图,则必须手动刷新绘图调用。 Cocoa不会自动使用从辅助线程绘制的内容更新视图,因此在完成绘图时需要调用NSGraphicsContext的flushGraphics方法。如果您的应用程序仅从主线程中提取内容,则无需刷新绘图调用。
致电flushGraphics
后,工作正常。