Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Issue with CFArrayRef and NSArray when drawing gradient using ARC

I have an ARC project and am trying to draw a vertical linear gradient. The code below works on the simulator, but throws a memory /EXC_BAD_ACCESS error when testing on the device. The app crashes on with the following two lines of code:

NSArray *colorArray = [NSArray arrayWithObjects:(__bridge id)topColor, (__bridge id)bottomColor, nil];
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef) colorArray, colorLocations); 

Those two lines of code are taken from the following code (provided for reference):

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

    [self createGradientForContext:context andView:self.captionView];
    [self createGradientForContext:context andView:self.linksView];
    [self createGradientForContext:context andView:self.commentView]; 

}

- (void)createGradientForContext:(CGContextRef)context andView:(UIView *)view
{

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

    CGFloat colorLocations[] = { 0.0f, 1.0f };

    CGColorRef topColor = [[UIColor colorWithRed:51.0f/255.0f green:51.0f/255.0f blue:51.0f/255.0f alpha:1.0f] CGColor];
    CGColorRef bottomColor = [[UIColor colorWithRed:48.0f/255.0f green:48.0f/255.0f blue:48.0f/255.0f alpha:1.0f] CGColor];
    NSArray *colorArray = [NSArray arrayWithObjects:(__bridge id)topColor, (__bridge id)bottomColor, nil];
    CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge_retained CFArrayRef) colorArray, colorLocations);    

    CGRect frame = view.frame;
    CGPoint startPoint = CGPointMake(CGRectGetMidX(frame), CGRectGetMinY(frame));
    CGPoint endPoint = CGPointMake(CGRectGetMidX(frame), CGRectGetMaxY(frame));

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

    CGGradientRelease(gradient);
    CGColorSpaceRelease(colorSpace);

}

Thanks ahead of time for any and all guidance.

like image 819
ArtSabintsev Avatar asked May 08 '12 01:05

ArtSabintsev


1 Answers

Now to explain why you were crashing…

The problem is these two lines:

CGColorRef topColor = [[UIColor colorWithRed:51.0f/255.0f green:51.0f/255.0f blue:51.0f/255.0f alpha:1.0f] CGColor];
CGColorRef bottomColor = [[UIColor colorWithRed:48.0f/255.0f green:48.0f/255.0f blue:48.0f/255.0f alpha:1.0f] CGColor];

This is a very common problem; there are many questions on Stack Overflow by users who've gotten bitten by this.

The problem is that because those UIColor objects are never referenced after those lines, ARC releases them immediately—and because those UIColor objects are the sole owners of the CGColor objects, the UIColors release the CGColors immediately, leaving you with two dead CGColor objects. Which you then try to put into an array.

As you found, switching from NSArray to pure CFArray will not fix this. Switching from UIColor to pure CGColor is one solution; the other is to put your own ownership on the CGColors:

CGColorRef topColor = CGColorRetain([[UIColor colorWithRed:51.0f/255.0f green:51.0f/255.0f blue:51.0f/255.0f alpha:1.0f] CGColor]);
CGColorRef bottomColor = CGColorRetain([[UIColor colorWithRed:48.0f/255.0f green:48.0f/255.0f blue:48.0f/255.0f alpha:1.0f] CGColor]);

⋮

CGColorRelease(topColor);
CGColorRelease(bottomColor);
like image 74
Peter Hosey Avatar answered Oct 04 '22 03:10

Peter Hosey