CG Gradient在模拟器上运行,但在iPhone上运行

时间:2011-11-16 17:18:24

标签: objective-c ios core-graphics automatic-ref-counting

我有一个编译没有问题的代码。它在iPhone模拟器上运行良好,但在我的设备上,我得到了一个EXC_BAD_ACCESS。

这在辅助函数中发生以绘制渐变。我跟着this tutorial去做了。我的代码如下:

- (void) drawRect:(CGRect)rect
{
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGColorRef whiteColor = [UIColor whiteColor].CGColor;
    CGColorRef lightGrayColor = [UIColor colorWithRed:230.0/255.0
                                                green:230.0/255.0 
                                                 blue:230.0/255.0 
                                                alpha:1.0].CGColor;
    CGColorRef separatorColor = [UIColor colorWithRed:208.0/255.0
                                                green:208.0/255.0 
                                                 blue:208.0/255.0
                                                alpha:1.0].CGColor;
    CGRect paperRect = self.bounds;
    CGRect nameRect = self.nameLabel.frame;
    CGPoint sepStartPoint = CGPointMake(nameRect.origin.x, 
                                        nameRect.origin.x + nameRect.size.height + 2);
    CGPoint sepEndPoint = CGPointMake(nameRect.origin.x + nameRect.size.width, 
                                      nameRect.origin.x + nameRect.size.height + 2);

    drawLinearGradient(context, paperRect, lightGrayColor, whiteColor);
    draw1PxStroke(context, sepStartPoint, sepEndPoint, separatorColor);

}


// Callee, where the problem is
void drawLinearGradient(CGContextRef context,
                        CGRect rect,
                        CGColorRef startColor, 
                        CGColorRef endColor)
{
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGFloat locations[] = { 0.0, 1.0 };

    NSArray *colors = [NSArray arrayWithObjects:
                       (__bridge id)startColor,
                       (__bridge id)endColor,
                       nil]; // Here is the line

    CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, 
                                                        (__bridge CFArrayRef) colors, locations);

    CGPoint startPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect));
    CGPoint endPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect));

    CGContextSaveGState(context);
    CGContextAddRect(context, rect);
    CGContextClip(context);
    CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);
    CGContextRestoreGState(context);

    CGGradientRelease(gradient);
    CGColorSpaceRelease(colorSpace);
}

Xcode突出显示第12行(nil];为错误行。

对于Peter Hosey,这是调试器输出:

(gdb) po startColor
<CGColor 0x1deca0> [<CGColorSpace 0x1d3280> (kCGColorSpaceDeviceGray)] ( 1 1 )
Current language:  auto; currently objective-c
(gdb) po endColor
<CGColorSpace 0x1bf120> (kCGColorSpaceDeviceRGB)
(gbd)

我的模拟器(和iPhone)在iOS 5上运行。

可能导致此次崩溃的原因是什么?

2 个答案:

答案 0 :(得分:14)

解决此问题的一种方法是将UIColors传递给函数,而不是CGColorRefs,并对(id)[color1 CGColor]数组的每个元素使用colors强制转换。这似乎是人们现在最常用的解决这个问题的方式。

我在this answer中指出了一个用法,并在this Apple developer forum thread中对此进行了扩展讨论。如果您在声明NSArray时使用UIColor的-CGColor方法,并转换为id,则所有内容都会自动为您桥接。正如gparker在上面链接的论坛帖子中所述:

  

本文档描述的自动案例仅适用于   调用返回CF类型的Objective-C方法然后   立即将结果转换为Objective-C对象类型。如果你   使用方法结果执行任何其他操作,例如将其分配给a   CF类型的变量,然后它不再是自动的。

正如hatfinch所指出的,这可能意味着放置在临时变量中的CGColorRefs在您最后一次引用UIColors之后不会挂起,除非您明确地保留它们。和那个论坛帖子中的其他人一样,我错误地认为这是桥接实现中的一个错误,但我可以看到我读错了。

答案 1 :(得分:12)

你没有让whiteColor和lightGrayColor保持活着。您从UIColors获得永远不会保留的CGColorRefs。您的代码应为:

CGColorRef whiteColor = CFRetain([UIColor whiteColor].CGColor);
CGColorRef lightGrayColor = CFRetain([UIColor colorWithRed:230.0/255.0 green:230.0/255.0 blue:230.0/255.0 alpha:1.0].CGColor);
CGColorRef separatorColor = CFRetain([UIColor colorWithRed:208.0/255.0 green:208.0/255.0 blue:208.0/255.0 alpha:1.0].CGColor);

// ...

drawLinearGradient(context, paperRect, lightGrayColor, whiteColor);
draw1PxStroke(context, sepStartPoint, sepEndPoint, separatorColor);

CFRelease(whiteColor);
CFRelease(lightGrayColor);
CFRelease(separatorColor);

您可以请求Apple声明 - [UIColor CGColor]为objc_returns_inner_pointer,这会使您的代码更简单,但该属性实际上是为不可保留的指针保留的。