Мне было интересно, можете ли вы использовать cancel / cancelAllOperations / .isCancelled с потоком, который вы запустили с помощью GCD?
В настоящее время я просто использую логическое значение в качестве флага, чтобы отменить фоновый процесс .
Допустим, вы хотите выполнять большую часть обработки в фоновом режиме, сохраняя при этом отзывчивость пользовательского интерфейса, чтобы можно было поймать кнопку отмены (или анимировать что-то, чтобы показать, что процессор работает). Вот как мы это делаем ...
@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;
}
и CHECKER не делает ничего, кроме проверки, установлен ли флаг ...
#define CHECKER if ( pleaseAbandonYourEfforts == YES ) \
{NSLog(@"Amazing Interruption System Working!");return;}
Все работает отлично.
Но ........ это можно ли использовать cancel / cancelAllOperations / .isCancelled с этим типом использования GCD?
Что здесь за история? Ура.
PS - для новичков, использующих этот фоновый шаблон из шести частей.
Обратите внимание, что, как BJ подчеркивает ниже, всякий раз, когда вы прерываете процесс bg ...
В моем идиоме вы должны выделить всю переменную, контексты, память и т. Д., В частности, в "setUpHere". И вы должны выпустить их в «wrapUpHere». (Эта идиома продолжает работать, если вы идете все глубже и глубже, находясь в BG.)
В качестве альтернативы, делайте именно то, что BJ показывает в своем примере. (Если вы используете метод BJ, будьте осторожны, если пойдете глубже.)
Какой бы метод вы ни использовали, вы должны очистить все открытые переменные / контексты / память, когда вы прерываете процесс BG. Надеюсь, это когда-нибудь поможет кому-нибудь!