Pouvez-vous utiliser annuler/isCancelled avec GCD/dispatch_async?
Je me demandais, vous pouvez utiliser annuler/cancelAllOperations/.isCancelled avec un sujet que vous avez lancé avec PGCD?
Actuellement, je viens d'utiliser un booléen comme un drapeau, d'annuler le processus en arrière-plan.
Disons que vous voulez faire beaucoup de traitement en arrière-plan, tout en gardant l'INTERFACE utilisateur réactive, de sorte que vous pouvez attraper un bouton annuler (ou animer quelque chose pour montrer que le processeur travaille). Voici comment nous le faisons...
@interface AstoundingView : UIView
{
BOOL pleaseAbandonYourEfforts;
blah
}
@implementation AstoundingView
//
//these are the foreground routines...
//begin, abandon and all-done
//
-(void)userHasClickedToBuildASpaceship
{
[YourUIStateMachine buildShip];
[self procedurallyBuildEnormousSpaceship];
}
-(void)userHasClickedToAbandonBuildingTheSpaceship
{
[YourUIStateMachine inbetween];
pleaseAbandonYourEfforts = false; //that's it!
}
-(void)attentionBGIsAllDone
{
//you get here when the process finishes, whether by completion
//or if we have asked it to cancel itself.
[self typically setNeedsDisplay, etc];
[YourUIStateMachine nothinghappening];
}
//
//these are the background routines...
//the kickoff, the wrapper, and the guts
//
//The wrapper MUST contain a "we've finished" message to home
//The guts can contain messages to home (eg, progress messages)
//
-(void)procedurallyBuildEnormousSpaceship
{
//user has clicked button to build new spaceship
pleaseAbandonYourEfforts = FALSE;
dispatch_async(
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0),
^{ [self actuallyProcedurallyBuildInBackground]; }
);
//as an aside, it's worth noting that this does not work if you
//use the main Q rather than a global Q as shown.
//Thus, this would not work:
//dispatch_async(dispatch_get_main_queue(), ^{ ...; });
}
-(void)actuallyProcedurallyBuildInBackground
{
//we are actually in the BG here...
[self setUpHere];
//set up any variables, contexts etc you need right here
//DO NOT open any variables, contexts etc in "buildGuts"
//when you return back here after buildGuts, CLEAN UP those
//variables, contexts etc at this level.
//(using this system, you can nest as deep as you want, and the
//one CHECKER pseudocall will always take you right out.
//You can insert CHECKERs anywhere you want.)
[self buildGuts];
//Note that any time 'CHECKER' "goes off', you must fall-
//through to exactly here. This is the common fall-through point.
//So we must now tidy-up, to match setUpHere.
[self wrapUpHere];
//when you get to here, we have finished (or, the user has cancelled
//the background operation)
//Whatever technique you use,
//MAKE SURE you clean up all your variables/contexts/etc before
//abandoning the BG process.
//and then you must do this......
//we have finished. it's critical to let the foreground know NOW,
//or else it will sit there for about 4 to 6 seconds (on 4.3/4.2)
//doing nothing until it realises you are done
dispatch_sync(
dispatch_get_main_queue(),
^{[self attentionBGIsAllDone];} //would setneedsdisplay, etc
);
return;
}
-(void)buildGuts
{
//we are actually in the BG here...
//Don't open any local variables in here.
CHECKER
[self blah blah];
CHECKER
[self blah blah];
CHECKER
[self blah blah];
//to get stuff done from time to time on the UI, something like...
CHECKER
dispatch_sync(
dispatch_get_main_queue(),
^{[supportStuff pleasePostMidwayImage:
[UIImage imageWithCGImage:halfOfShip] ];}
);
CHECKER
[self blah blah];
CHECKER
[self blah blah];
CHECKER
[self blah blah];
for ( i = 1 to 10^9 )
{
CHECKER
[self blah blah];
}
CHECKER
[self blah blah];
CHECKER
[self blah blah];
CHECKER
[self blah blah];
return;
}
et le VÉRIFICATEUR ne fait rien de plus que de vérifier le drapeau est vrai...
#define CHECKER if ( pleaseAbandonYourEfforts == YES ) \
{NSLog(@"Amazing Interruption System Working!");return;}
Tout cela fonctionne parfaitement.
Mais ........ est-il possible d'utiliser annuler/cancelAllOperations/.isCancelled avec ce type d'utilisation de PGCD?
Quelle est l'histoire ici? Des acclamations.
PS - pour tout les débutants à l'aide de ce "six la partie" modèle d'arrière-plan.
Noter que, comme BJ faits saillants ci-dessous, chaque fois que vous briser le processus de bg...
vous devez nettoyer toutes les variables que vous avez ouvert!
Dans mon idiome, vous devez allouer tous les variable, de contextes, de la mémoire, etc, plus précisément dans "setUpHere". Et vous devez les libérer dans "wrapUpHere". (Cet idiome continue de travailler si vous allez plus loin et plus profond, tandis que dans le BG.)
Alternativement, faire exactement ce que BJ montre dans son exemple. (Si vous utilisez BJ méthode, être prudent, si vous allez plus loin.)
Quelle que soit la méthode que vous utilisez, vous devez nettoyer toutes les variables/contextes/mémoire que vous avez ouvert, lorsque vous briser le processus de BG. Espérons que cela aide quelqu'un, un jour!
buildGuts
méthode, vous devez vous assurer de les relâcher dans la méthode, avant de sortir de la méthode et de perdre le pointeur à ces objets.J'ai ajouté une autre mise à jour de clarification dans ma déclaration sur le seul point de retour. Votre
[self quickly wrap up in a bow]
appeler pas de gérer toute la gestion de la mémoire qui peut être nécessaire, et tout simplement s'assurer qu'une certaine méthode sera exécutée après la buildGuts
méthode ne permet pas de le résoudre. Quand je me réfère à un seul point de retour, je voulais dire qu'il n'y a qu'un seul endroit où la méthode de sortie. En vertu de votre mise en œuvre, il peut sortir de n'importe où le CHECKER
macro est utilisée. Ce n'est pas un seul point de retour.Salut BJ, j'ai ajouté quelques commentaires expliquant simplement que dans l'exemple de code, cheers!
Juste pour être ultra clair, comme il est dit "// Ne pas ouvrir les variables locales de ici." .. c'est la fin de celui-ci et la plupart de très bonne façon d'éviter d'ouvrir des variables dans les il y! heh.
Un peu en retard pour la fête mais pleaaase, ne pas utiliser l'auto à l'intérieur du PGCD de bloc, cela va créer un cycle de conserver et de rendre votre vue sera conservé pour toujours.... utiliser un __faibles pointeur pour accéder à la place de la variable... salutations
OriginalL'auteur Fattie | 2011-03-27
Vous devez vous connecter pour publier un commentaire.
PGCD n'a pas de prise en charge intégrée de l'annulation; si c'est important d'être en mesure d'annuler votre tâche en arrière-plan, puis de vérifier un drapeau comme vous l'avez démontré, c'est une solution acceptable. Cependant, vous voudrez peut-être évaluer la façon dont rapidement l'annulation doit répondre; si certains de ces appels de méthodes sont assez courts, vous pourriez être en mesure de s'en tirer avec la vérification de moins en moins fréquemment.
Vous avez demandé si vous pouviez utiliser le NSOperation drapeaux à l'appui de l'annulation. La réponse est non. PGCD n'est pas basé sur NSOperation. En fait, dans Snow Leopard NSOperation et NSOperationQueue ont été re-mises en œuvre pour utiliser le PGCD en interne. Si la dépendance est l'inverse. NSOperation est d'un plus haut niveau de la construction que PGCD. Même si vous étiez à utiliser NSOperation, cependant, votre mise en œuvre de l'annulation seraient en grande partie le même; il vous faudrait toujours vérifier
self.isCancelled
périodiquement pour voir si vous devez abandonner le navire de l'espace de construction.Le seul souci que j'ai avec votre mise en œuvre de la
CHECKER
macro, c'est qu'il met en œuvre un inattendureturn
. En tant que tel, vous devez être prudent au sujet des fuites de mémoire. Si vous avez mis en place votre propre NSAutoreleasePool sur le thread d'arrière-plan, vous devezdrain
avant de retourner. Si vous avezalloc
ed ouretain
ed de tous les objets, vous devrez peut-êtrerelease
avant de redescendre.Depuis tous que le nettoyage doit se produire à chaque case, vous devrez peut-être envisager d'aller vers un seul point de retour. Une façon de le faire est de les envelopper chacun de vos appels de méthode dans un
if (pleaseAbandonYourEfforts == NO) { }
bloc. Ce serait vous permettent de rapidement de l'automne jusqu'à la fin de la méthode une fois que l'annulation a été demandée, et de garder votre nettoyage en un seul endroit. Une autre option, mais il se peut que le détester, serait de faire de la macro d'appelgoto cleanup;
et de définir uncleanup:
étiquette près de la fin de la méthode où vous relâchez tout ce qui doit être libéré. Certaines personnes détestent en utilisantgoto
dans une quasi-religieuse, mais j'ai trouvé que l'avant de sauter à un nettoyage de l'étiquette, comme ce est souvent une solution plus propre que les solutions de rechange. Si vous ne l'aimez pas, enveloppant le tout dans uneif
bloc fonctionne tout aussi bien.Modifier
Je ressens le besoin de préciser davantage ma déclaration précédente sur le fait d'avoir un seul point de retour. Avec le
CHECKER
macro tel que défini ci-dessus, le-buildGuts
méthode peut renvoyer à n'importe quel point où cette macro est utilisée. S'il y a des conservées des objets locaux à cette méthode, ils doivent être nettoyés avant de retourner. Par exemple, imaginez ce très raisonnable modification de votre-buildGuts
méthode:Noter que dans ce cas, si le
CHECKER
macro nous fait rentrer avant la fin de la méthode, l'objet dansformatter
ne sera pas publié et sera coulé. Alors que le[self quickly wrap up in a bow]
appel peut gérer le nettoyage de tous les objets accessibles par le biais d'une variable d'instance ou par le biais d'un pointeur global, il ne peut pas libérer des objets qui n'étaient disponibles localement dans lebuildGuts
méthode. C'est pourquoi j'ai proposé l'goto cleanup
mise en œuvre, qui devrait ressembler à ceci:En vertu de cette mise en œuvre,
formatter
sera toujours libéré, indépendamment du moment de l'annulation se produit.En bref, à chaque fois que vous mave une macro qui peut vous causer de retour d'une méthode, vous devez être très sûr que avant prématurément retour, toute la gestion de la mémoire a été pris en charge. Il est difficile de le faire proprement avec une macro qui provoque un retour.
Salut BJ .. concernant le nettoyage de toutes les variables locales lorsque vous cassez vers le haut: c'est sûr. J'ai ajouté des commentaires dans le code d'exemple pour expliquer mon idiome.
OriginalL'auteur BJ Homer
Merci pour la discussion! Dans mon cas, je voulais permettre de délivrer une nouvelle demande asynchrone qui permettrait d'annuler la précédente s'il n'avait pas encore terminée. Avec l'exemple ci-dessus, je dois en quelque sorte, attendre un signal via le
attentionBGIsAllDone
rappel que l'encours de la demande a été annulée avant que je puisse faire une nouvelle demande. Au lieu de cela, j'ai créé un simple booléen wrapper, une instance de qui je pourrais associer à une demande en suspens:Et utiliser une instance de cette pour
pleaseAbandonYourEfforts
. Avant, je fais de mondispatch_async
(c'est à dire dansprocedurallyBuildEnormousSpaceship
ci-dessus), j'annule la vieille demande et de se préparer pour l'un comme suit:Mon bloc de l'exécution de la tâche asynchrone faudrait vérifier
cancelThisRequest.value
de cours.OriginalL'auteur qix