display vs. setNeedsDisplay

时间:2012-06-25 09:36:23

标签: cocoa osx-lion quartz-2d

我注意到Mac OS X 10.7上基于Cocoa的应用程序存在一个奇怪的问题。 由于某些原因(这里不重要),有时我必须在自定义视图的drawRect方法之外绘制。

我必须在我的视图上调用lockFocus / lockFocusIfCanDraw,询问当前上下文,用CGContext函数系列实现绘图(CoreGrapchis),最后再做CGContextFlush(我也可以刷新窗口,或者使用NSGraphicsContext类做冲洗的方法。)

这个序列实际上和我调用NSView的-display方法一样。

问题是......它比“自然”方式慢3-4倍(当Cocoa要求你这样做时,调用setNeedsDisplay或从drawRect中绘制)。 我不能简单地为一个视图调用setNeedsDisplay,我需要这个'-display - like'功能。

在一个测试示例(使用计时器)中,为了简单起见,我调用-display(因为它通常与我的应用程序的工作相同)vs -setNeedsDisplay,我可以看到'-display'的时间是'-setNeedsDisplay'的3-4倍。

以下是我的CustomView类(实现)的示例:

#import <QuartzCore/QuartzCore.h>

#import "CustomView.h"

@implementation CustomView
{
   CFTimeInterval startTime;
   NSTimer *timer;
   unsigned step;
}

- (id)initWithFrame:(NSRect)frame
{
    return [super initWithFrame : frame];
}

- (void)drawRect:(NSRect)dirtyRect
{
   CGContextRef ctx = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];

   if(!timer)
   {
      CGContextSetRGBFillColor(ctx, 1., 1., 1., 1.);
      CGContextFillRect(ctx, dirtyRect);
   }
   else
   {
      CGContextSetRGBFillColor(ctx, 0., 0., 0., 1.);
      CGContextFillRect(ctx, CGRectMake(step * 1.5, 100, 2., 2.));
   }
}

- (void) mouseDown : (NSEvent *)theEvent
{
   if (!timer)
   {
      startTime = CACurrentMediaTime();
      timer = [NSTimer scheduledTimerWithTimeInterval : 0.006 target : self selector : @selector(handleTimer:) userInfo : nil repeats : YES];
      step = 0;
   }
}

- (void) handleTimer : (NSTimer *) dummy
{
   if(step < 200)
   {
      ++step;
#if 1
      [self display];
#else
      [self setNeedsDisplay : YES];
#endif
   }
   else 
   {
      [timer invalidate];
      timer = nil;
      NSLog(@"animation time is: %g", CACurrentMediaTime() - startTime);
   }
}

@end

我认为即使CACurrentMediaTime对我来说不是很好的功能,它仍然可以显示明显的时差(并且很容易注意到没有任何测量 - 显示非常慢)。 handleTimer方法有两个部分 - 如果你在pp-directive中将'1'改为'0',你可以尝试-display / -setNeedsDisplay。所以,我有,例如以下输出:

- 显示:3.32秒。 (?)

-setNeedsDisplay:1.2 s。

我查看了“乐器”应用程序生成的调用树/花费的时间,但它对我没什么帮助。

编辑: 嗯,我现在可以看到:实际上,在每个计时器事件中都没有重新绘制setNeedsDisplay视图!

1 个答案:

答案 0 :(得分:0)

无需下拉到drawRect方法中的CG函数。

此代码等同于:

- (void)drawRect:(NSRect)dirtyRect
{
   if(!timer)
   {
      [[NSColor whiteColor] set];
      NSRectFill(dirtyRect);
   }
   else
   {
      [[NSColor blackColor] set];
       NSRectFill(NSMakeRect(step * 1.5, 100.0, 2.0, 2.0));
   }
}

对于-display和-setNeedsDisplay,前者使绘图立即发生,后者设置一个标志,每次通过事件循环,如果该标志为真,窗口将发送-display到相关视图并清除旗帜。

还有一件事:这种使用NSTimer来驱动动画的方法有点过时了。你应该阅读关于核心动画的文档来学习如何做这种事情。