5

Tengo un proyecto ARC y estoy tratando de dibujar un degradado lineal vertical. El siguiente código funciona en el simulador, pero arroja un error de memoria/EXC_BAD_ACCESS cuando se prueba en el dispositivo. La aplicación se bloquea en las siguientes dos líneas de código:Problema con CFArrayRef y NSArray al dibujar degradado usando ARC

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

Esas dos líneas de código se toman de la siguiente código (con la condición de referencia):

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

} 

Gracias de antemano por cualquier y toda orientación.

+0

¿No te gusta el arco? Quitando el control del programador de la memoria ... pensé que Apple aprendió su lección con la recolección de basura. Yo recomendaría no usarlo ... –

+3

Recomendaría usar ARC, realmente tiene que haber una razón convincente para no hacerlo. – zaph

+0

¿Por qué está usando un objeto allí cuando todo lo demás es un 'CFType'? Simplemente use 'CFArray' y no tiene que preocuparse por la creación de puentes. –

Respuesta

5

Intente utilizando un CFArrayRef y evite los puentes y vea si hace la diferencia.

CGFloat topColorComps[] = {51.0f/255.0f, 51.0f/255.0f, 51.0f/255.0f, 1.0f}; 
CGFloat bottomColorComps[] = {48.0f/255.0f, 48.0f/255.0f, 48.0f/255.0f, 1.0f};    

CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB(); 
CGColorRef topColor = CGColorCreate(rgb, topColorComps); 
CGColorRef bottomColor = CGColorCreate(rgb, bottomColorComps); 
CGColorSpaceRelease(rgb); 

CFMutableArrayRef colorArray = CFArrayCreateMutable(NULL, 2, &kCFTypeArrayCallbacks); 
CFArrayAppendValue(colorArray, topColor); 
CFArrayAppendValue(colorArray, bottomColor); 

CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, colorArray, colorLocations); 

CFRelease(colorArray); 
CFRelease(topColor); 
CFRelease(bottomColor); 
+0

Gracias por su ayuda. Lamentablemente, se bloquea en 'CFArrayAppendValue (colorArray, topColor)' cuando se ejecuta en mi dispositivo. – ArtSabintsev

+0

@ArtSabintsev: El problema podría ser el uso de 'UIColor'. He editado el ejemplo anterior, esta vez, eres el dueño de todo. – dreamlax

+0

gracias! Ahora estoy obteniendo 'CGColorCreateGenericRGB no está disponible'. Mirando esto. – ArtSabintsev

7

Ahora que explicar por qué estaban rompiendo ...

El problema es que estos dos líneas:

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

Este es un problema muy común; hay muchas preguntas sobre Stack Overflow por parte de los usuarios que se han mordido por esto.

El problema es que debido a que esos objetos UIColor nunca se referencian después de esas líneas, ARC los libera inmediatamente y porque esos objetos UIColor son los únicos propietarios de los objetos CGColor, los UIColors liberan los CGColors inmediatamente, dejándote con dos muertos Objetos CGColor. Que luego intentas poner en una matriz.

Como ha encontrado, cambiar de NSArray a CFArray puro no solucionará esto. El cambio de UIColor a CGColor puro es una solución; el otro es poner su propia propiedad sobre los 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); 
+3

+1 para una respuesta técnica correcta. Para mí, una mejor solución es usar UIColor hasta el momento de crear la matriz: [NSArray arrayWithObjects: (__ ID del puente) startColour.CGColor, (__bridge id) endColour.CGColor, nil]; – Cthutu

+0

@Cthutu: Eso hace lo mismo que retenerlos usted mismo; en ese caso, la matriz los conserva. Para propósitos sin gradiente, pasar 'myUIColor.CGColor' directamente a' CGContextSetFillColorWithColor' o similar (en lugar de almacenarlo en una variable 'CGColorRef' y luego recuperarlo desde allí) también debería funcionar, si esa función retiene el objeto de color o no intenta guardarlo. –

+0

Sí, es exactamente lo mismo pero no necesita todas las llamadas CGColorRelease(). La liberación se realiza en ARC y no en una llamada de Core Foundation. – Cthutu

Cuestiones relacionadas