在NSView中绘制多个线程

dop*_*ime 5 macos cocoa multithreading objective-c nsview

在我的代码中,我进行了子类化,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.

}
Run Code Online (Sandbox Code Playgroud)

当我从Xcode运行相同的程序时,它运行正常.输出如下所示. 单线程

但是当我从外面运行它时,有问题,输出如下所示. 多线程

首先,我想知道从二级线程中绘制是否正确?或者从辅助线程中提取的另一种方法是什么?

这个问题背后的原因是什么?

Bra*_*red 7

Ken Aspeslagh对于从辅助线程中绘制有些不正确(他是正确的,这通常是一个坏主意).从我所看到的代码中你没有一个很好的用例来绘制辅助线程.你能解释一下你为什么要这样做吗?

你自己已经发现了setCanDrawConcurrently:哪个明确地讨论了drawRect:从后台线程调用.请注意,视图窗口必须allowsConcurrentViewDrawing设置为YES才能使其生效(这是默认设置).

苹果自己的Cocoa绘图指南中有一个关于从辅助线程绘制的部分.我已经强调了一些我认为与你相关的部分.

Application Kit为每个窗口和线程组合维护一个独特的图形上下文.因为每个线程都有自己的给定窗口的图形上下文对象,所以可以使用辅助线程绘制到该窗口.但是,有一些警告.

在Windows的正常更新周期中,所有绘图请求都将发送到应用程序的主线程进行处理.当用户事件触发用户界面的更改时,会发生正常的更新周期.在这种情况下,您可以从应用程序的主线程调用setNeedsDisplay:或setNeedsDisplayInRect:方法(或显示系列方法),以使视图中需要重绘的部分无效.您不应该从任何辅助线程调用这些方法.

如果要从辅助线程更新窗口或视图,则必须手动将焦点锁定在窗口或视图上并自行启动绘图.锁定焦点为该窗口的图形上下文配置绘图环境.锁定后,您可以配置绘图环境,照常发出绘图命令,然后将图形上下文的内容刷新到窗口缓冲区.

为了在辅助线程上定期绘制,您必须自己通知线程.发送常规通知的最简单方法是使用NSTimer或NSAnimation对象.有关如何为内容设置动画的详细信息,请参阅"高级绘图技术".

Cocoa线程编程指南也说:

如果要使用线程绘制视图,请在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];
});
Run Code Online (Sandbox Code Playgroud)

但是,这可能与您的问题无关; 我之所以提到它只是因为我认为它会更好地为你提供GCD队列.

  • 重要说明:将`canDrawConcurrently`设置为`YES`意味着**`drawRect:`本身将在后台线程上调用.**您不需要,也不应该在其他线程上调用多个其他绘图方法然后回来. (2认同)
  • 请注意,在实践中,并发绘图通常不是性能获胜.处理此问题的正确方法是使用图层支持的视图和-updateLayer而不是-drawRect:.许多动画等不仅可以异步发生,而且可以在进程外(至少在Mavericks中)发生. (2认同)

dop*_*ime 1

NSGraphicsContext Restriction线程指南中读到了相关内容。

在这里,我发现了以下行:

如果您从辅助线程进行任何绘图,则必须手动刷新绘图调用。Cocoa 不会自动使用从辅助线程绘制的内容来更新视图,因此当您完成绘制时,您需要调用 NSGraphicsContext 的lushGraphics 方法。如果您的应用程序仅从主线程绘制内容,则无需刷新绘图调用。

调用后flushGraphics,效果很好。