Compiler/runtime ARC issue assigning [(UIColor *) CGColor] to CAGridentLayer

Originator:benmartz
Number:rdar://10241227 Date Originated:11/5/2011
Status:Open Resolved:
Product:Xcode Product Version:4.1
Classification: Reproducible:Yes
 
05-Oct-2011 03:11 PM Ben Martz:
Summary:

After migrating my iOS 4 project to iOS 5 and ARC, I struggled to figure out how to properly assign an array of color values to the colors property of a CAGradientLayer. I also discovered that my first attempt to work around the issue resulted in code that worked correctly in the simulator but not on a real device.

I found the following thread on the iOS 5 Beta forum which got me pointed in the correct direction to resolve my issue: https://devforums.apple.com/message/550670

Steps to Reproduce:

CAGradientLayer *gradientLayer = [[CAGradientLayer alloc] init];		

CGColorRef topColor = [[UIColor colorWithRed:0.933333333333333 green:0.933333333333333 blue:0.941176470588235 alpha:1.0] CGColor];
CGColorRef bottomColor = [[UIColor colorWithRed:0.827450980392157 green:0.827450980392157 blue:0.850980392156863 alpha:1.0] CGColor];

// #1 COMPILER ERROR: Cast of C pointer type 'CGColorRef' to Objective-C pointer type 'id' requires a bridged cast
gradientLayer.colors = [NSArray arrayWithObjects:(id)topColor, (id)bottomColor, nil];

// #2 Works in correctly on iPad 5.0 simulator (Xcode 4.2 GM), however throws EXC_BAD_ACCESS on physical 1st gen iPad device running iOS 5.0 GM
gradientLayer.colors = [NSArray arrayWithObjects:(__bridge id)topColor, (__bridge id)bottomColor, nil];

// #3 The explicit inline cast of UIColor property CGColor to id appears to be the ONLY way to make this bridged assignment
// compile and generate code that works correctly on both the simulator and a real device
gradientLayer.colors = [NSArray arrayWithObjects:
     (id)[[UIColor colorWithRed:0.933333333333333 green:0.933333333333333 blue:0.941176470588235 alpha:1.0] CGColor],
     (id)[[UIColor colorWithRed:0.827450980392157 green:0.827450980392157 blue:0.850980392156863 alpha:1.0] CGColor],
     nil];

Expected Results:

Examples #1 and #3 should be considered identical by the compiler. The current requirement that values be provided inline breaks generally accepted best practices of code and constant value reuse.

The equivalence of example #2 is unknown since I can not claim to be 100% clear on the different bridging modes. Nonetheless, IMO, example #2 should demonstrate identical results on both the simulator and a physical device.

Actual Results:

Provided inline with steps to reproduce.

Regression:

n/a

Notes:

n/a

05-Oct-2011 03:22 PM Ben Martz:
Continuing my work, I have found that even a simple color assignment* as follows works correctly on the simulator but DOES NOT work on a physical device:

* declare CGColorRef variable; assign CGColorRef to variable; assign variable to [CAGradientLayer backgroundColor] property

- (void)drawRect:(CGRect)rect
{
	if ([self isHighlighted] != wasHighlighted)
	{
		wasHighlighted = [self isHighlighted];
		
		CGColorRef shadowColor, highlightColor, topColor, bottomColor;

		if ([self isHighlighted])
		{
			shadowColor = [[UIColor colorWithRed:0.356862745098039 green:0.36078431372549 blue:0.380392156862745 alpha:1.0] CGColor];
			highlightColor = [[UIColor colorWithRed:0.850980392156863 green:0.854901960784314 blue:0.870588235294118 alpha:1.0] CGColor];
			topColor = [[UIColor colorWithRed:0.701960784313725 green:0.709803921568627 blue:0.737254901960784 alpha:1.0] CGColor];
			bottomColor = [[UIColor colorWithRed:0.513725490196078 green:0.52156862745098 blue:0.549019607843137 alpha:1.0] CGColor];
		}
		else
		{
			shadowColor = [[UIColor colorWithRed:0.513725490196078 green:0.513725490196078 blue:0.525490196078431 alpha:1.0] CGColor];
			highlightColor = [[UIColor whiteColor] CGColor];
			topColor = [[UIColor colorWithRed:0.933333333333333 green:0.933333333333333 blue:0.941176470588235 alpha:1.0] CGColor];
			bottomColor = [[UIColor colorWithRed:0.827450980392157 green:0.827450980392157 blue:0.850980392156863 alpha:1.0] CGColor];
		}
		
		// EXC_BAD_ACCESS on physical device
		[shadowLayer setBackgroundColor:shadowColor];

		// EXC_BAD_ACCESS on physical device
		[highlightLayer setBackgroundColor:highlightColor];

		// force instant response animation
		CABasicAnimation* selectionAnimation = [CABasicAnimation animationWithKeyPath:@"colors"];
		selectionAnimation.delegate = self;
		selectionAnimation.duration = 0;
		selectionAnimation.fillMode = kCAFillModeForwards;
		selectionAnimation.removedOnCompletion = false;
		
		selectionAnimation.fromValue = (id)[NSArray arrayWithObjects:(__bridge id)topColor, (__bridge id)bottomColor, nil];
		selectionAnimation.toValue = (id)[NSArray arrayWithObjects:(__bridge id)topColor, (__bridge id)bottomColor, nil];
		
		[gradientLayer addAnimation:selectionAnimation forKey:@"selectionAnimation"];
	}
	
	[super drawRect:rect];	
}

Comments


Please note: Reports posted here will not necessarily be seen by Apple. All problems should be submitted at bugreport.apple.com before they are posted here. Please only post information for Radars that you have filed yourself, and please do not include Apple confidential information in your posts. Thank you!