App crashes when using __bridge for CoreGraphics g

2019-05-24 21:21发布

问题:

I'm creating an application for iOS 5 and I'm drawing some gradients. The following gradient code I've always used before ARC, but now it does not work on my device anymore (however, it works on the simulator) when I use it several times (so I suppose it's a memory management issue). Anyways, here's the code:

CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGFloat locations[] = { 0.0, 1.0 };

NSArray *colors = [NSArray arrayWithObjects:(__bridge id)startColor, (__bridge id)endColor, nil];

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);

Originally, there were no __bridge statements, I added them as suggested by Xcode. What exactly is causing the problem?

回答1:

Your issue might be with the lifetime of the startColor, etc. variables. I'm guessing that these might be CGColorRefs created via UIColor's -CGColor method at some point above the code you've listed.

As I describe in this answer, unless you explicitly retain those CGColorRefs, they may go away after the UIColor that generated them has been deallocated. Given that you never use the UIColors again after you've extracted the CGColorRefs from them, ARC may decide to deallocate these UIColors before you've had a chance to use the CGColorRefs. I've seen object lifetimes differ between the Simulator and actual devices, so this could explain the crash on one but not the other.

My solution to this has been to use an immediate cast to id, like in the following:

NSArray *colors = [NSArray arrayWithObjects:(id)[color1 CGColor],
                                            (id)[color2 CGColor], nil];

where the compiler does the right thing as far as transferring ownership of the CGColorRefs.

There's also the possibility that your NSArray is being deallocated early, in which case the following code might make sure it hangs around long enough:

NSArray *colors = [NSArray arrayWithObjects:(__bridge id)startColor, (__bridge id)endColor, nil];

CFArrayRef colorArray = (__bridge_retained CFArrayRef)colors;
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, colorArray, locations);
CFRelease(colorArray);

This bridges the NSArray across to Core Foundation, leaving behind a CFArrayRef with a retain count of at least 1. You can then use that in your gradient creation, where the gradient will hopefully keep a reference to it, and release it manually when done.

However, Bringo's suggestion to work entirely within Core Graphics' C API for this might be the easiest way to go here. I just thought I'd explain a potential source of your problems, in case you run into something similar in the future.



回答2:

I ran into this exact same issue. I have resorted to using CGGradientCreateWithColorComponents which solves the problem for me. You have to convert your NSArray of CGColorRefs to an array of CGFloats.

CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGFloat locations[2] = { 0.0, 1.0 };

CGFloat components[8] = { 0.909, 0.909, 0.909, 1.0,  // Start color
                          0.698f, 0.698f, 0.698f, 1.0 }; // End color

CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, components,
                                     locations, 2);