J'ai des problèmes avec l'avertissement suivant:
CoreAnimation: avertissement, thread supprimé avec CATransaction non validée; définissez CA_DEBUG_TRANSACTIONS = 1 dans l'environnement pour consigner les backtraces.
J'utilise un objet NSOperation pour effectuer certains calculs, une fois terminé, il renvoie un message à l'AppDelegate qui masque ensuite une barre de progression et affiche certains boutons. Si je commente le message à l'AppDelegate, l'avertissement disparaît mais la barre de progression reste évidemment visible et animée.
J'utilise xCode 4.4.1 et OSX 10.8.1, cependant, lorsque je compile et exécute le code en utilisant la même version de xCode sur OSX 10.7.4, je ne reçois pas l'avertissement et le code s'exécute comme prévu.
La définition de la variable d'environnement CA_DEBUG_TRANSACTIONS = 1 affiche la trace arrière comme provenant d'un message NSControl setEnabled dans AppDelegate.
La réponse me regarde probablement en face, mais j'ai peut-être trop bu de café!
Vos soupçons sont bons. Si NSOperation se termine avant l'exécution de CoreAnimation, vous obtenez un avertissement de Nice:
* CoreAnimation: avertissement, thread supprimé avec CATransaction non engagé; définissez CA_DEBUG_TRANSACTIONS = 1 dans l'environnement pour consigner les traces. *
Cela peut également se produire dans certaines circonstances lorsqu'un bloc qui est distribué dans une file d'attente déclenche un travail à partir de CoreAnimation et retourne avant la fin de CoreAnimation.
La solution que j'utilise est simple: sur un bloc ou NSOperation qui demande du travail à CoreAnimation, je vérifie que le travail est bien terminé avant de quitter.
Pour vous donner un exemple de preuve de concept, il s'agit d'un bloc à distribuer dans une file d'attente de répartition. Afin d'éviter l'avertissement, nous vérifions que la CoreAnimation est effectuée avant de quitter.
^{
// 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);
}
}
Conformément aux paradigmes Cocoa standard, la solution recommandée ici consiste à effectuer votre travail d'animation de base sur le thread principal, facilement effectué avec GCD:
dispatch_async(dispatch_get_main_queue(), ^{
[self.delegate redrawSomething];
});
En général, c'est une mauvaise forme d'appeler des objets dans des contextes auxquels ils ne s'attendent pas, donc une bonne règle est de toujours envoyer sur le thread principal lors de la livraison de messages à des modules externes.
Certains frameworks, comme Core Location, émettent un message de journal s'ils sont appelés à partir de n'importe quel contexte autre que le thread principal. D'autres émettront des messages cryptés, comme votre exemple ici avec Core Animation.
Une autre façon de garantir qu'un dessin d'interface utilisateur se produit sur le thread principal, comme décrit par Numist, consiste à utiliser la méthode performSelectorOnMainThread:withObject:waitUntilDone:
Ou alternativement 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.
}
Pour un article connexe sur la différence entre dispatch_async()
et performSelectorOnMainThread:withObjects:waitUntilDone:
Voir Quelle est la différence entre performSelectorOnMainThread et dispatch_async sur la file d'attente principale?