2011-11-16 20 views
7

Ho un codice che viene compilato senza problemi. Funziona bene con il simulatore di iPhone, ma sul mio dispositivo, ho un EXC_BAD_ACCESS.CG Il gradiente funziona su simulatore, ma non su iPhone

Questo succede in una funzione di supporto per disegnare il gradiente. Ho seguito this tutorial per farlo. Il codice che ho è come segue:.

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

Xcode mette in evidenza la linea 12 (quella con nil]; come la linea di errore

Per Peter Hosey, ecco l'output del debugger:

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

mio simulatore (e iPhone) funziona su iOS 5.

Cosa potrebbe causare questo arresto?

+0

Ciò probabilmente significa che uno di startColor o endColor è un puntatore pendente; puoi mostrare il codice che chiama drawLinearGradient? – Tommy

+0

Ho aggiornato l'elenco con il codice di chiamata – ksol

+0

Se si digita 'po startColor' e' po endColor' nella console di debugger, cosa ottieni? –

risposta

14

Un modo per aggirare questo problema consiste nel passare UIColors nella funzione, anziché in CGColorRefs, e utilizzare un cast (id)[color1 CGColor] per ciascun elemento dell'array colors. Questo sembra essere il modo più popolare in cui le persone affrontano questo problema al momento.

Indicare un utilizzo di questo in this answer e la discussione estesa su questo argomento è this Apple developer forum thread. Se si utilizza un metodo UIColor -CGColor al momento di dichiarare il proprio NSArray e si esegue il cast sull'id, tutto viene automaticamente colmato per te. Come afferma gparker del sopra-linked thread del forum:

Il caso automatica descritta dalla documentazione si applica solo ai chiamando un metodo di Objective-C che restituisce un tipo CF e poi subito lanciare il risultato a un Objective- Tipo di oggetto C Se si usa il risultato del metodo , ad esempio assegnarlo a una variabile di un tipo di CF, non sarà più automatico.

Come indica il ciuffo, questo potrebbe significare che i tuoi CGColorRef inseriti in variabili temporanee non si bloccheranno dopo l'ultima volta che fai riferimento ai tuoi UIColors, a meno che tu non li mantenga esplicitamente. Come gli altri in quel thread del forum, ho erroneamente pensato che fosse un bug nell'implementazione di bridging, ma posso vedere che stavo leggendo questo errore.

+2

Si tratta di un bug ARC, comunque? [The ARC spec] (http://clang.llvm.org/docs/AutomaticReferenceCounting.html) afferma che i riferimenti agli oggetti CF non sono considerati "puntatori di oggetti conservabili", il che implica che ARC non intende conservare i CGColors. Gli UIColors non sono referenziati dopo che i CGColors sono stati estratti da essi, quindi non esiste una ragione attendibile attendibile per aspettarsi che vivano più a lungo di quello. Se gli UIColors possiedono i CGColors, porteranno con sé i CGColors quando vengono deallocati. –

+1

La soluzione potrebbe funzionare, ma non perché ARC ha un bug; si comporta come previsto (vedi il commento di Peter e la mia risposta). – hatfinch

+0

@PeterHosey - Ah, non proprio un bug, perché mi mancava questo nella parte superiore del thread del forum collegato: "Il caso automatico descritto dalla documentazione si applica solo alla chiamata di un metodo Objective-C che restituisce un tipo di CF e quindi esegue immediatamente il casting il risultato in un tipo di oggetto Objective-C. Se si fa qualcos'altro con il risultato del metodo, ad esempio assegnandolo a una variabile di un tipo CF, non sarà più automatico. " Sembrava un bug, a causa dell'uso diretto all'interno del lavoro NSArray, ma non del compito temporaneo CGColorRef. Il metodo '-CGColor' sembra essere un caso speciale. –

12

Non stai mantenendo in vita i tuoi colori bianco e chiaro. Ottieni CGColorRefs che non possiedi da UIColors che non vengono mai conservati. Il codice dovrebbe leggere:

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

Si potrebbe presentare una petizione per dichiarare di Apple - [UIColor CGColor] come objc_returns_inner_pointer, che renderebbe il vostro codice più semplice, ma che attributo è davvero riservato per i puntatori non a conservazione.

+0

Questo ha molto più senso dell'ipotesi errata che io (e altri) abbiamo avuto che si trattasse di un bug ARC. Sembra che il casting diretto su un NSObject all'uscita dal metodo '-CGColor' sia un caso speciale, e questo mi ha causato un po 'di confusione qui. –

Problemi correlati