我在try 绘制路径,我注意到至少在某些情况下,UIBezierPath的性能超过了我认为的Core Graphics等效项.下面的-drawRect:方法创建两条路径:一条UIBezierPath和一条CGPath.这些路径除了它们的位置之外都是相同的,但是描边CGPath所需的时间大约是描边UIBezierPath的两倍.

- (void)drawRect:(CGRect)rect
{
    CGContextRef ctx = UIGraphicsGetCurrentContext();

    // Create the two paths, cgpath and uipath.
    CGMutablePathRef cgpath = CGPathCreateMutable();
    CGPathMoveToPoint(cgpath, NULL, 0, 100);

    UIBezierPath *uipath = [[UIBezierPath alloc] init];
    [uipath moveToPoint:CGPointMake(0, 200)];

    // Add 200 curve segments to each path.
    int iterations = 200;
    CGFloat cgBaseline = 100;
    CGFloat uiBaseline = 200;
    CGFloat xincrement = self.bounds.size.width / iterations;
    for (CGFloat x1 = 0, x2 = xincrement;
         x2 < self.bounds.size.width;
         x1 = x2, x2 += xincrement)
    {
        CGPathAddCurveToPoint(cgpath, NULL, x1, cgBaseline-50, x2, cgBaseline+50, x2, cgBaseline);
        [uipath addCurveToPoint:CGPointMake(x2, uiBaseline)
                  controlPoint1:CGPointMake(x1, uiBaseline-50)
                  controlPoint2:CGPointMake(x2, uiBaseline+50)];
    }
    [[UIColor blackColor] setStroke];
    CGContextAddPath(ctx, cgpath);

    // Stroke each path.
    [self strokeContext:ctx];
    [self strokeUIBezierPath:uipath];

    [uipath release];
    CGPathRelease(cgpath);
}

- (void)strokeContext:(CGContextRef)context
{
    CGContextStrokePath(context);
}

- (void)strokeUIBezierPath:(UIBezierPath*)path
{
    [path stroke];
}

这两条路径都使用CGContextStrokePath(),因此我创建了单独的方法来描边每条路径,以便可以在Instruments中查看每条路径所用的时间.以下是典型结果(调用树颠倒);您可以看到,-strokeContext:需要9.5秒,而-strokeUIBezierPath:只需要5秒:

Running (Self)      Symbol Name
14638.0ms   88.2%               CGContextStrokePath
9587.0ms   57.8%                 -[QuartzTestView strokeContext:]
5051.0ms   30.4%                 -[UIBezierPath stroke]
5051.0ms   30.4%                  -[QuartzTestView strokeUIBezierPath:]

看起来UIBezierPath正在以某种方式优化它创建的路径,或者我正在以一种天真的方式创建CGPath.我能做些什么来加速我的CGPath绘图?

推荐答案

您说得对,UIBezierPath只是Core Graphics的Objective-c包装器,因此性能相当.不同之处(以及性能差值的原因)在于直接绘制CGPath时的CGContext状态与设置为UIBezierPath时的状态有很大不同.如果您查看UIBezierPath,它有以下设置:

  • lineWidth
  • lineJoinStyle
  • lineCapStyle
  • miterLimit
  • flatness

在判断对[path stroke]的调用(反汇编)时,您会注意到,在执行CGContextStrokePath调用之前,它会根据这些先前的值配置当前图形上下文.如果您在绘制CGPath之前执行相同的操作,它将执行相同的操作:

- (void)drawRect:(CGRect)rect
{
    CGContextRef ctx = UIGraphicsGetCurrentContext();

    // Create the two paths, cgpath and uipath.
    CGMutablePathRef cgpath = CGPathCreateMutable();
    CGPathMoveToPoint(cgpath, NULL, 0, 100);

    UIBezierPath *uipath = [[UIBezierPath alloc] init];
    [uipath moveToPoint:CGPointMake(0, 200)];

    // Add 200 curve segments to each path.
    int iterations = 80000;
    CGFloat cgBaseline = 100;
    CGFloat uiBaseline = 200;
    CGFloat xincrement = self.bounds.size.width / iterations;
    for (CGFloat x1 = 0, x2 = xincrement;
         x2 < self.bounds.size.width;
         x1 = x2, x2 += xincrement)
    {
        CGPathAddCurveToPoint(cgpath, NULL, x1, cgBaseline-50, x2, cgBaseline+50, x2, cgBaseline);
        [uipath addCurveToPoint:CGPointMake(x2, uiBaseline)
                  controlPoint1:CGPointMake(x1, uiBaseline-50)
                  controlPoint2:CGPointMake(x2, uiBaseline+50)];
    }
    [[UIColor blackColor] setStroke];
    CGContextAddPath(ctx, cgpath);

    // Stroke each path
    CGContextSaveGState(ctx); {
        // configure context the same as uipath
        CGContextSetLineWidth(ctx, uipath.lineWidth);
        CGContextSetLineJoin(ctx, uipath.lineJoinStyle);
        CGContextSetLineCap(ctx, uipath.lineCapStyle);
        CGContextSetMiterLimit(ctx, uipath.miterLimit);
        CGContextSetFlatness(ctx, uipath.flatness);
        [self strokeContext:ctx];
        CGContextRestoreGState(ctx);
    }
    [self strokeUIBezierPath:uipath];

    [uipath release];
    CGPathRelease(cgpath);
}

- (void)strokeContext:(CGContextRef)context
{
    CGContextStrokePath(context);
}

- (void)strokeUIBezierPath:(UIBezierPath*)path
{
    [path stroke];
}

来自Instruments的快照: 显示相同性能的仪器快照

Ios相关问答推荐

Xcode15.3上的生成错误:调用的对象类型';facebook::flipper::SocketCertificateProvider';不是函数或函数指针

如何遮罩,动画和添加阴影到CAShapeLayer?

滚动时突出显示工具栏项目

更改文本在Flutter 中的位置

无法在所有窗口上方显示图像

将数据传回VC时委托为空

iOS应用程序冻结在UICollectionView代码,但我没有';没有任何UICollectionViews

WatchConnectivity - 从手表配套应用程序在 iOS 设备上启动 AVPlayer

当 .searchable 修饰符处于活动状态时,如何将变量设置为 false?

当任何 TextEditor 有多于一行时,所有 TextEditor 都会调整大小

ionic 4 - 编译 ios - 来自项目Pods的目标GoogleDataTransport中的问题

从 PHAssets 生成图像时,iOS 应用程序因内存问题而崩溃

VStack 显示错误实例方法 'sheet(item:onDismiss:content:)' 要求 'Int' 符合 'Identifiable'

用溢出的长文本对齐 Flutter 中的行和列

SensorKit - 获取数据不调用结果委托函数

Xcode 8:函数类型不能有参数标签 destruct 我的构建

iOS中的apk类似功能是什么?

如何确定 WKWebView 的内容大小?

判断密钥是否存在于 NSDictionary 中

更新字段时,UITextField值已更改未触发