2011-11-16 17 views
7

Tengo un código que compila sin problemas. Funciona bien en el simulador de iPhone, pero en mi dispositivo, obtengo un EXC_BAD_ACCESS.CG Gradient se ejecuta en el simulador, pero no en el iPhone

Esto sucede en una función auxiliar para dibujar degradado. Seguí this tutorial para hacerlo. El código que tengo es el siguiente:.

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

destacados Xcode línea 12 (el que tiene nil]; como la línea de error

Para Peter Hosey, aquí está la salida de un depurador:

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

Mi simulador (y iPhone) se ejecuta en iOS 5.

¿Qué podría estar causando este bloqueo?

+0

Eso probablemente significa que uno de startColor o endColor es un puntero colgante; ¿Puedes mostrar el código que llama a drawLinearGradient? – Tommy

+0

Actualicé la esencia con el código de llamada – ksol

+0

Si escribe 'po startColor' y' po endColor' en la consola del depurador, ¿qué obtiene? –

Respuesta

14

Una forma de evitar esto sería pasar UIColors en su función, en lugar de CGColorRefs, y usar un molde (id)[color1 CGColor] para cada elemento de su matriz colors. Esta parece ser la forma más popular en que las personas abordan este problema en este momento.

Señalo un uso de esto en this answer, y hay una amplia discusión sobre esto en this Apple developer forum thread. Si usa el método -CGColor de UIColor en el momento de declarar su NSArray, y lo transfiere a la identificación, todo se cerrará automáticamente para usted. Como estados gparker en la anteriormente unido hilo del foro:

El caso automático se describe en la documentación se aplica únicamente a llamar a un método Objective-C que devuelve un tipo de CF y luego fundición inmediatamente el resultado a un Objetivo- Tipo de objeto C Si hace algo más con el resultado del método, como asignarlo a una variable de un tipo CF, entonces ya no es automático.

Como señala hatfinch, esto podría significar que su CGColorRefs colocado en variables temporales no se mantendrá después de la última referencia a sus UIColors, a menos que los retenga explícitamente. Al igual que los otros en ese hilo del foro, erróneamente pensé que esto era un error en la implementación del puente, pero puedo ver que estaba leyendo esto mal.

+2

¿Es eso realmente un error ARC? [La especificación ARC] (http://clang.llvm.org/docs/AutomaticReferenceCounting.html) dice que las referencias a objetos CF no se consideran "punteros de objetos retenibles", lo que implica que ARC no pretende retener los CGColors. Los UIColors no se referencian después de que los CGColors se extraen de ellos, por lo que no hay una razón confiable para suponer que vayan a vivir más tiempo. Si los UIColors poseen los CGColors, se llevarán los CGColors cuando se desasignaran. –

+1

Su solución funcionaría, pero no porque ARC tiene un error; se está comportando como debería (ver el comentario de Peter y mi respuesta). – hatfinch

+0

@PeterHosey - Ah, no es un error, porque me perdí esta cerca de la parte superior del hilo del foro vinculado: "El caso automático descrito por la documentación se aplica solo a llamar a un método Objective-C que devuelve un tipo CF y luego de inmediato el resultado para un tipo de objeto Objective-C. Si hace algo más con el resultado del método, como asignarlo a una variable de tipo CF, entonces ya no es automático ". Parecía un error, debido al uso directo dentro del trabajo NSArray, pero la asignación temporal CGColorRef no. El método '-CGColor' parece ser un caso especial. –

12

No mantendrá vivo su whiteColor y lightGrayColor. Obtiene CGColorRefs que no posee de UIColors que nunca se conservan. El código debería leer:

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

Usted puede presentar una petición de Apple para declarar - [UIColor CGColor] como objc_returns_inner_pointer, lo que haría que su código más simple, pero ese atributo es realmente reservado para los punteros no retainable.

+0

Eso tiene mucho más sentido que la suposición errónea que yo (y otros) tuve de que esto era un error de ARC. Parece que el lanzamiento directo a un NSObject al salir del método '-CGColor' es un caso especial, y eso me causó cierta confusión aquí. –

Cuestiones relacionadas