2012-02-09 2 views
7

So come risolvere il problema che sto per delineare, tuttavia, sono un po 'sconcertato sul perché lo scenario del codice funzioni nel simulatore iOS ma non sul mio iPad.L'assegnazione di un CGColor esistente a una proprietà CGColor funziona in iOS Simulator, non nel dispositivo iOS. Perché?

Ho un metodo che controlla varie proprietà e quindi imposta il colore di sfondo di un CALayer a seconda dello stato di una proprietà. Il seguente codice è simile al mio metodo di assegnazione dei colori:

//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. 

Il codice sopra funziona senza problemi nel simulatore. Tuttavia, quando eseguo l'applicazione sul mio iPad, si blocca al momento dell'assegnazione backgroundColor.

Posso risolvere questo problema eliminando la variabile CGColor e assegnando il colore di sfondo direttamente dall'istruzione switch/case, ed è ciò che sto pensando di fare.

Tuttavia, sono curioso. Perché dovrebbe funzionare in un ambiente e non nell'altro?

UPDATE

paio di cose. Innanzitutto, vale la pena ricordare che si tratta di un progetto ARC, che utilizza Xcode 4.2, destinato a dispositivi iOS 5. Inoltre, il mio codice di assegnazione del colore non è interamente quello che sembra perché ho una serie di definizioni che uso per impostare questi colori perché sono referenziati in tutta la mia applicazione.

Questo è ciò che alcune delle affermazioni #define assomiglia:

#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 

ho cercato di semplificare il mio codice perché il compilatore dovrebbe sostituire gli arbitri ai miei arbitri ai miei definisce. Comunque, vale la pena menzionarlo per ogni evenienza.

+0

Avete fatto sicuri di includere il quadro QuartzCore in ogni costruire? –

+1

Il mio non sarà nemmeno costruito con il tuo 'CGColor', forse intendevi' CGColorRef' –

+0

Ho aggiunto il framework e ho fatto riferimento alle intestazioni all'interno del file di codice che sta effettuando questo compito. – RLH

risposta

8

Ecco la mia impressione: è possibile che lo UIColor che lo ha creato (e che abbia mantenuto il suo unico riferimento) sia stato distrutto prima di passare lo CGColor. Dal momento che il conteggio dei riferimenti CGColorRef non viene gestito per te sotto ARC, il colore sarebbe un riferimento ciondolante se lo UIColor che lo conteneva è stato distrutto prima di utilizzare lo CGColor.

ARC dispone di un'ottimizzazione in cui gli oggetti "autoreleased" non possono mai essere aggiunti a pool di autorelease e invece, released dopo che l'oggetto objc non è più referenziato. Questa è una combinazione di tre elementi:

  1. La versione del compilatore e le opzioni che si utilizzano. Nessuna sorpresa, il compilatore aggiunge il conteggio dei riferimenti e ci sono variazioni per questo.
  2. The ObjC Runtime. Il runtime può utilizzare i dati locali del thread. Naturalmente, questo può includere il tuo stack.Se leggi i dettagli di come un oggetto può bypassare un pool di autorelease, questo dovrebbe essere più chiaro.
  3. Le librerie utilizzate (comprese librerie di sistema e framework). Con l'aggiornamento del compilatore e dei runtime, le librerie possono utilizzare ARC, oppure possono utilizzare diverse chiamate di runtime per eseguire il programma.

Sapendo che, ho il sospetto che questo programma sarebbe risolvere il problema:

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. 

Più in dettaglio, ci sono molti modi del compilatore e al runtime objc può interpretare ed eseguire il programma. Ciò significa che questo problema potrebbe influire su di te quando cambi le versioni del compilatore o quando il runtime (OS) viene aggiornato. Può anche accadere mentre le librerie utilizzate vengono aggiornate o create con versioni diverse o impostazioni del compilatore. Ad esempio: se la libreria passa a ARC lungo la strada, potrebbe utilizzare una diversa chiamata di runtime oppure le chiamate potrebbero utilizzare i dati locali del thread in modo diverso se le chiamate iniettate dal compilatore vengono aggiornate.

dettagli circa le specifiche ARC in quanto si riferisce al tempo di esecuzione può essere trovato qui: http://clang.llvm.org/docs/AutomaticReferenceCounting.html#runtime


Un problema simile è stato visto qui:

EXC_BAD_ACCES drawing shadow

+0

Ignora il messaggio che ho appena cancellato. Magia! Questo ha risolto il problema. Ci sono differenze significative nel modo in cui QuartzCore gestisce la memoria su OSX da iOS? Questa non è la prima volta che vedo delle piccole, stranezze nella gestione della memoria tra i due, ma questo è certamente stato il più strano. – RLH

+0

@RLH Non è Quartz - ha a che fare con ARC e con il compilatore (questa è la mia impressione - non utilizzo ancora ARC in progetti reali e non ho letto l'ultima implementazione del runtime objc per confermare questo @ 100%) . Il runtime utilizza l'archiviazione locale del thread in alcuni casi; oggetti che * dovrebbero * essere autorelasciati in MRC potrebbero non essere mai aggiunti a un pool di autorelease. Invece, il compilatore aggiungerebbe quello che risulterebbe in un messaggio di rilascio in alcuni runtime (naturalmente, questo succederebbe dopo aver finito di usarlo).Ho aggiunto un link alla mia risposta per ulteriori dettagli sulla sua implementazione. – justin

+0

Grazie per il collegamento e ho contrassegnato questa come risposta perché dubito che ci sia una risposta diretta (discernibile), a meno che qualcuno qui intorno non lavori con il codice proprietario Apple per questi framework o compilatori. Il tuo articolo sembra piuttosto approfondito ... lo controllerò questo fine settimana. – RLH

0

Non si dice quale sia l'istanza myCGLayer derivata da, ma eseguirò una ripresa e dirò che non è derivata da CGLayer, poiché CGLayer non ha una proprietà backgroundColor. Quindi sto indovinando (di nuovo), il parametro passato dovrebbe essere di tipo UIColor e non CGColor. CGColor è derivato dalla classe CFType. UIColor deriva da NSObject. Non dovrebbero essere intercambiabili. Se le mie ipotesi sono corrette, sono sorpreso che funzioni nel simulatore.

Non schiaffeggiarmi troppo se le mie ipotesi sono sbagliate.

+0

Probabilmente intendevano CALayer. Hanno effettivamente detto CALAYER nella descrizione. – Chuck

+0

Grazie per lo sforzo, ma quello è stato un mio errore. Ho digitato tutto il codice di esempio, quindi, non taglia e incolla. Chuck ha ragione - questo è un "CALAYER". Ho aggiornato la mia variabile nell'esempio per riflettere questo. – RLH

+0

Ciò farebbe la differenza (se il codice reale non ha quel tipo di errore). – Jim

4

A causa della ARC del colore viene rilasciato troppo presto alla fine del metodo.

che uso: CGColorRetain

CGColorRef whiteColor = CGColorRetain([UIColor colorWithRed:1.0 green:1.0 
             blue:1.0 alpha:1.0].CGColor);