【问题标题】:UIBezierPath drawing takes up 100% of CPUUIBezierPath 绘制占用 100% 的 CPU
【发布时间】:2015-01-14 21:57:25
【问题描述】:

每次用户触摸移动时,我都会在屏幕上绘制一个 UIBezierPath。这在我的 Mac Pro 上的模拟器上运行良好,但是一旦我将它移到物理设备上,绘图就开始滞后很多。使用仪器,我检查了 CPU 使用率,在用户绘图时它达到了 100%。

这成为一个很大的问题,因为我有一个 NSTimer,它应该以 30 fps 的速度触发,但是当 CPU 因绘图而过载时,计时器仅以 10 fps 左右的速度触发。

如何优化绘图使其不占用 100% 的 CPU?

UITouch* lastTouch;

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    if(lastTouch != nil)
        return;

    lastTouch = [touches anyObject];
    [path moveToPoint:[lastTouch locationInView:self]];
    [self drawDot];
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    if([touches anyObject] == lastTouch)
        [self drawDot];
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    if([touches anyObject] == lastTouch)
        lastTouch = nil;
}

- (void)drawDot
{
    if(!self.canDraw)
        return;

    [path addLineToPoint:[lastTouch locationInView:self]];
    [self setNeedsDisplayInRect:CGRectMake([lastTouch locationInView:self].x-30, [lastTouch locationInView:self].y-30, 60, 60)];
}

- (void)drawRect:(CGRect)rect
{    
    CGColorRef green = [UIColor green].CGColor;
    CGContextRef context = UIGraphicsGetCurrentContext();

    CGColorRef gray = [UIColor gray].CGColor;
    CGContextSetStrokeColor(context, CGColorGetComponents(gray));
    [shape stroke];

    CGContextSetStrokeColor(context, CGColorGetComponents(green));
    [path stroke];
}

【问题讨论】:

  • 路径使用 100% 时有多大(多少线段)?

标签: ios objective-c core-graphics drawrect uibezierpath


【解决方案1】:

您真的不应该在现代代码中使用-drawRect - 它已有 30 年历史,并且专为非常旧的硬件(25Mhz CPU,16MB RAM 和无 GPU)而设计,并且在现代硬件上存在性能瓶颈。

相反,您应该使用 Core Animation 或 OpenGL 进行所有绘图。 Core Animation 可能与 drawRect 非常相似。

将此添加到视图的 init 方法中:

self.layer.contentsScale = [UIScreen mainScreen].scale;

并实施:

- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx
{
   // basically the same code as you've got inDrawRect, although I recommend
   // trying Core Graphics (CGContextMoveToPoint(), CGContextAddLineToPoint(), 
   // CGContextStrokePath(), etc)
}

并使其重新绘制(在 touchesMoved/etc 内):

[self.layer setNeedsDisplay];

我还将更新您的触摸事件代码以仅附加到点的NSMutableArray(可能编码在[NSValue valueWithCGPoint:location] 中)。这样您就不会在响应触摸事件时创建图形路径。

【讨论】:

  • 所以CA使用GPU而不是cpu进行绘图?
  • @David 我认为所有绘图都在 iOS 的 GPU 上。您的 [path stroke] 在内部使用 Core Animation,但它也执行其他操作以保持向后兼容性。
  • 基本上,drawRect 只执行一次或两次就可以了。你永远不应该让它快速连续执行多次。当您的视图首次出现在屏幕上或响应单击事件时触发 drawRect 时很好。它不应该响应触摸移动事件进行任何绘图。
【解决方案2】:

寻找高效drawRect的好地方:是几年前的wwdc视频:https://developer.apple.com/videos/wwdc/2012/?id=238。大约 26m 时,他开始调试一个非常简单的绘画应用程序,该应用程序与您所描述的非常相似。在第 30 分钟,他在 setNeedsDisplayInRect 的第一次优化之后开始优化:(你已经在做)。

小故事:他使用图像作为已绘制内容的后备存储,因此每个 drawRect: 调用他只绘制 1 个图像 + 非常短的附加线段,而不是每次都绘制非常长的路径。

【讨论】:

    猜你喜欢
    • 2012-12-29
    • 1970-01-01
    • 1970-01-01
    • 2022-12-15
    • 2011-11-09
    • 2020-03-22
    • 2012-03-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多