Можно ли использовать cancel / isCancelled с GCD / dispatch_async?

Мне было интересно, можете ли вы использовать 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. Надеюсь, это когда-нибудь поможет кому-нибудь!

7
задан Fattie 25 April 2011 в 20:18
поделиться