Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Assigning an existing CGColor to a CGColor property works in iOS Simulator, not iOS device. Why?

I know how to fix the problem that I am about to outline, however, I am a bit baffled as to why the code scenario works in the iOS simulator but not on my iPad.

I have a method that checks various properties, and then sets the background color of a CALayer depending on the state of a property. The following code is similar to my method of color assignment:

//This will be the CALayer BGColor...
CGColor c = UIColor.blueColor.CGColor; //Blue is the default
switch (myState)
{
    case state_one:
        c = UIColor.greenColor.CGColor;
        //... more code ...
        break;
    case state_two:
        c = UIColor.redColor.CGColor;
        //... more code ...
        break;
    case state_three: //multiple cases are like the state_three case.
        //Other code, but I don't need to assign the color.  Blue works...
}

myCALayer.backgroundColor = c; //Oh-noes!!! Here we get the dreaded EXC_BAD_ACCESS on iPad
//...more code dealing with the layer.

The code above works without trouble in the Simulator. However, when I run the application on my iPad, it crashes at the backgroundColor assignment.

I can fix this by getting rid of the CGColor variable and assigning the background color from directly within my switch/case statement, and that's what I'm planning on doing.

However, I am curious. Why would this work in one environment and not the other?

UPDATE

Couple things. First, it's worth mentioning that this is an ARC project, using Xcode 4.2, targeting iOS 5 devices. Also, my color assignement code isn't entirely what it looks like because I have a series of defines that I use to set these colors because they are referenced all throughout my application.

This is what a few of the #define statements looks like:

#define BLUE  [UIColor colorWithRed:8.0/255.0 green:80.0/255.0 blue:150.0/255.0 alpha:1.0].CGColor
#define GREEN (UIColor.blueColor.CGColor)
//...and there are about 6 other colors

I tried to simplify my code because the compiler should replace the refs to my refs to my defines. Still, it's worth mentioning just in case.

like image 468
RLH Avatar asked Feb 09 '12 20:02

RLH


2 Answers

Because of ARC the color is released too early in the end of the method.

i use: CGColorRetain

CGColorRef whiteColor = CGColorRetain([UIColor colorWithRed:1.0 green:1.0
                                         blue:1.0 alpha:1.0].CGColor);
like image 163
ugiflezet Avatar answered Nov 16 '22 01:11

ugiflezet


Here's my hunch: It's possible that the UIColor that created it (and held its only reference) has been destroyed before you pass the CGColor. Since CGColorRef's reference counting is not handled for you under ARC, the color would be a dangling reference if the UIColor that held it were destroyed before you use the CGColor.

ARC has an optimization where "autoreleased" objects may never be added to an autorelease pools, and instead, released after the objc object is no longer referenced. This is a combination of three things:

  1. The compiler version and options you use. No surprise, the compiler adds the reference counting, and there are variations for this.
  2. The ObjC Runtime. The runtime may utilize thread local data. Naturally, this can include your stack. If you read into the details of how an object may bypass an autorelease pool, this should be clearer.
  3. The libraries you use (including system libraries and frameworks). As the compiler and runtimes are updated, the libraries may use ARC, or they may use different runtime calls to execute the program.

Knowing that, I suspect this program would rectify the problem:

UIColor * c = UIColor.blueColor; //Blue is the default
switch (myState) {
    case state_one:
        c = UIColor.greenColor;
        //... more code ...
        break;
    case state_two:
        c = UIColor.redColor;
        //... more code ...
        break;
    case state_three: //multiple cases are like the state_three case.
        //Other code, but I don't need to assign the color.  Blue works...
}

myCGLayer.backgroundColor = c.CGColor;
//...more code dealing with the layer.

In more detail, there are number of ways the compiler and the objc runtime can interpret and execute your program. This means that this problem could affect you when you change compiler versions, or when the runtime (OS) is updated. It can also happen as the libraries you use are updated or built with different versions or compiler settings. For example: If the library switches to ARC along the way, it may use a different runtime calls, or the calls may utilize thread local data differently if the compiler injected calls are updated.

Details about the ARC spec as it relates to the runtime can be found here: http://clang.llvm.org/docs/AutomaticReferenceCounting.html#runtime


A similar problem was seen here:

EXC_BAD_ACCES drawing shadow

like image 43
justin Avatar answered Nov 16 '22 02:11

justin