Me he estado preguntando, ¿puedes usar cancel/cancelAllOperations/.isCancelled con un hilo que has iniciado con GCD?¿Se puede usar cancel/isCancelled con GCD/dispatch_async?
Actualmente, solo uso un booleano como indicador, para cancelar el proceso en segundo plano.
Digamos que desea hacer un montón de procesamiento en segundo plano, mientras mantiene la IU receptiva para que pueda capturar un botón cancelar (o animar algo para mostrar que el procesador está funcionando). Así es como lo hacemos ...
@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;
}
y INSPECTOR no hace más que comprobar que la bandera es cierto ...
#define CHECKER if (pleaseAbandonYourEfforts == YES) \
{NSLog(@"Amazing Interruption System Working!");return;}
Todo esto funciona a la perfección.
Pero ........ ¿es posible usar cancelar/cancelar todas las operaciones/.isCancelado con este tipo de uso de GCD?
¿Cuál es la historia aquí? Aclamaciones.
PD - para cualquier principiante que utilice esta plantilla de fondo de "seis partes".
Tenga en cuenta que como BJ destaca a continuación, cada vez que sales del proceso de BG ...
debe limpiar cualquier variable que tiene abiertas!
En mi idioma, debe asignar todas las variables, contextos, memoria, etc., específicamente en "setUpHere". Y debe liberarlos en "wrapUpHere". (Este idioma sigue funcionando si profundizas más y más, mientras estás en el BG.)
O bien, haz exactamente lo que BJ muestra en su ejemplo. (Si usa el método de BJ, tenga cuidado si profundiza)
Cualquiera sea el método que utilice, debe limpiar cualquier variable/contexto/memoria que tenga abierta, cuando salga del proceso de BG. Espero que ayude a alguien, alguna vez!
Creo que no entendió mi punto acerca del punto de retorno único. Si asigna objetos temporales en el método 'buildGuts', debe asegurarse de liberarlos en ese método, antes de salir del método y perder el puntero a esos objetos. –
Agregué otra actualización que aclaraba más mi declaración acerca del punto de retorno único. Tu '[autocompletar rápidamente en una reverencia]' llamada * no puede * manejar toda la administración de memoria que pueda ser necesaria, y simplemente asegurarte de que se ejecutará cierto método después de que el método 'buildGuts' no lo arregle. Cuando me refiero a un único punto de retorno, quise decir que solo hay un lugar donde el método puede salir. Bajo su implementación, puede salir a cualquier parte donde se use la macro 'CHECKER'. Ese no es un solo punto de retorno. –
Hola BJ, agregue algunos comentarios explicando solo eso en el ejemplo de código, ¡salud! – Fattie