2012-09-20 14 views
37

sto avendo problemi con il seguente avviso:avvertimento Core Animation cancellato filo con CATransaction impegnati

Core Animation: avvertimento, filo cancellato con CATransaction non impegnati; imposta CA_DEBUG_TRANSACTIONS = 1 in ambiente per registrare i backtrace.

Sto utilizzando un oggetto NSOperation per eseguire alcuni calcoli, una volta completato invia un messaggio a AppDelegate che nasconde una barra di avanzamento e visualizza alcuni pulsanti. Se commento nuovamente il messaggio all'AppDelegate, l'avviso scompare, ma la barra di avanzamento rimane ovviamente visibile e animata.

Sto utilizzando xCode 4.4.1 e OSX 10.8.1, tuttavia, quando compilo ed eseguo il codice utilizzando la stessa versione di xCode su OSX 10.7.4, non ricevo l'avviso e il codice viene eseguito come previsto.

L'impostazione della variabile di ambiente CA_DEBUG_TRANSACTIONS = 1 mostra il backtrace proveniente da un messaggio setEnabled di NSControl in AppDelegate.

La risposta probabilmente mi sta fissando in faccia ma forse ho bevuto troppo caffè!

+3

Ho giocato un po 'oggi con questo. Sospetto che il problema è che NSOperation viene completato prima che CoreAnimation abbia finito con il ridisegno degli elementi dell'interfaccia utente. Il backtrace ha mostrato il metodo originariamente chiamato da NSOperation. Ho provato a implementare una NSNotification da NSOperation per dire all'AppDelegate che il calcolo è completo, sperando che questo significherebbe che NSOperation può essere rilasciato senza influenzare CoreAnimation, tuttavia l'avvertimento si verifica ancora ma questa volta l'origine è CoreFoundation? – Milly

+1

In entrambi i casi il programma sembra funzionare come previsto e l'avviso viene visualizzato solo quando è in esecuzione su OS 10.8.1 o 10.8.2 – Milly

risposta

18

I tuoi sospetti sono corretti. Se NSOperation viene completato prima che CoreAnimation venga eseguito, viene visualizzato un messaggio di avviso:

* CoreAnimation: avviso, thread cancellato con CATransaction non salvata; imposta CA_DEBUG_TRANSACTIONS = 1 in ambiente per registrare i backtrace. *

Ciò può verificarsi anche in alcune circostanze quando un blocco che viene inviato su una coda attiva alcuni lavori da CoreAnimation e restituisce prima che CoreAnimation termini.

La soluzione che utilizzo è semplice: su un blocco o NSOperation che richiede lavoro da CoreAnimation, controllo che il lavoro sia stato effettivamente completato prima di uscire.

Per fornire un esempio di proof-of-concept, questo è un blocco da inviare su una coda di invio. Per evitare l'avvertimento, controlliamo che CoreAnimation sia fatto prima di uscire.

^{ 

    // 1. Creating a completion indicator 

    BOOL __block animationHasCompleted = NO; 

    // 2. Requesting core animation do do some work. Using animator for instance. 

    [NSAnimationContext runAnimationGroup:^(NSAnimationContext *context){ 
     [[object animator] perform-a-nice-animation]; 
    } completionHandler:^{ 
     animationHasCompleted = YES; 
    }]; 

    // 3. Doing other stuff… 

    … 

    // 4. Waiting for core animation to complete before exiting 

    while (animationHasCompleted == NO) 
    { 
     usleep(10000); 
    } 

} 
+3

Questa non è la soluzione migliore in quanto consuma risorse non necessarie. Vedi sotto per soluzioni migliori. In particolare, usando 'performSelectorOnMainThread: withObject: waitUntilDone:' o 'dispatch_async()'. – Dalmazio

+0

Questa è una soluzione non valida, tutte le animazioni dovrebbero essere inviate al thread principale. – PPierson

+3

@PPierson In realtà le animazioni vengono inviate al thread principale. Non vedo cosa c'è di sbagliato nella soluzione. – Jean

24

In linea con i paradigmi di cacao standard, la soluzione consigliata è quella di svolgere il proprio lavoro Core Animation sul thread principale, fatto facilmente con GCD:

dispatch_async(dispatch_get_main_queue(), ^{ 
    [self.delegate redrawSomething]; 
}); 

In generale è scarsa forma di chiamare gli oggetti in contesti che non si aspettano, quindi una buona regola empirica è quella di sempre inviare sul thread principale durante la consegna di messaggi a moduli esterni.

Alcuni Core di tipo Sede posizione - con un messaggio di registro se vengono chiamati da qualsiasi contesto diverso dal thread principale. Altri emetteranno messaggi criptici, come il tuo esempio qui con Core Animation.

+1

provato questo e non ha funzionato per me – rbp

8

Un altro modo per garantire qualsiasi disegno UI si verifica sul filo principale, come descritto da Numist, utilizza il metodo performSelectorOnMainThread:withObject:waitUntilDone: oppure performSelectorOnMainThread:withObject:waitUntilDone:modes:

- (void) someMethod 
{ 
    [...] 

    // Perform all drawing/UI updates on the main thread. 
    [self performSelectorOnMainThread:@selector(myCustomDrawing:) 
          withObject:myCustomData 
         waitUntilDone:YES]; 

    [...] 
} 

- (void) myCustomDrawing:(id)myCustomData 
{ 
    // Perform any drawing/UI updates here. 
} 


Per un post correlati sulla differenza tra dispatch_async() e performSelectorOnMainThread:withObjects:waitUntilDone: vedi Whats the difference between performSelectorOnMainThread and dispatch_async on main queue?

+1

soluzione semplice e buona :) –

Problemi correlati