Attendere che vengano eseguiti due blocchi asincroni prima di iniziare un altro blocco


192

Quando si utilizza GCD, vogliamo attendere che vengano eseguiti e eseguiti due blocchi asincroni prima di passare alle fasi successive dell'esecuzione. Qual è il modo migliore per farlo?

Abbiamo provato quanto segue, ma non sembra funzionare:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
    // block1
});


dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
    // block2
});

// wait until both the block1 and block2 are done before start block3
// how to do that?

dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
    // block3
});

Vedi la mia risposta per Swift 5 che offre fino a sei modi diversi per risolvere il tuo problema.
Imanou Petit,

Risposte:


301

Usa gruppi di invio: vedi qui un esempio, "In attesa di gruppi di attività in coda" nel capitolo "Code di spedizione" della Guida alla programmazione della concorrenza della libreria per sviluppatori iOS di Apple

Il tuo esempio potrebbe assomigliare a questo:

dispatch_group_t group = dispatch_group_create();

dispatch_group_async(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
    // block1
    NSLog(@"Block1");
    [NSThread sleepForTimeInterval:5.0];
    NSLog(@"Block1 End");
});


dispatch_group_async(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
    // block2
    NSLog(@"Block2");
    [NSThread sleepForTimeInterval:8.0];
    NSLog(@"Block2 End");
});

dispatch_group_notify(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
    // block3
    NSLog(@"Block3");
});

// only for non-ARC projects, handled automatically in ARC-enabled projects.
dispatch_release(group);

e potrebbe produrre output in questo modo:

2012-08-11 16:10:18.049 Dispatch[11858:1e03] Block1
2012-08-11 16:10:18.052 Dispatch[11858:1d03] Block2
2012-08-11 16:10:23.051 Dispatch[11858:1e03] Block1 End
2012-08-11 16:10:26.053 Dispatch[11858:1d03] Block2 End
2012-08-11 16:10:26.054 Dispatch[11858:1d03] Block3

3
Freddo. i task / blocchi asincroni, una volta associati al gruppo, saranno eseguiti in sequenza o contemporaneamente? Voglio dire, supponiamo che il blocco1 e il blocco2 siano associati a un gruppo ora, il blocco2 attenderà fino al completamento del blocco1 prima che possa iniziare l'esecuzione?
tom

9
Dipende da te. dispatch_group_asyncè proprio come dispatch_asynccon un parametro di gruppo aggiunto. Pertanto, se si utilizzano code diverse per block1 e block2 o si pianificano sulla stessa coda simultanea, è possibile eseguirle contemporaneamente; se li pianifichi sulla stessa coda seriale, verranno eseguiti in serie. Non è diverso dalla pianificazione dei blocchi senza gruppi.
Jörn Eyrich, l'

1
Questo vale anche per l'esecuzione di servizi web post?
SonnoNon

Noti che il tempo non è uguale al tempo di sonno impostato nel tuo blocco? perché sarebbe così?
Damon Yuan,

2
In ARC basta rimuovere dispatch_release (gruppo);
Loretoparisi,

272

Espandendo la risposta di Jörn Eyrich (vota la sua risposta se ne voti una), se non hai il controllo delle dispatch_asyncchiamate per i tuoi blocchi, come potrebbe essere il caso dei blocchi di completamento asincrono, puoi usare i gruppi GCD usando dispatch_group_entere dispatch_group_leavedirettamente.

In questo esempio, stiamo fingendo computeInBackgroundche non possiamo cambiare (immaginiamo che sia un callback delegato, NSURLConnection completamentoHandler o qualsiasi altra cosa), e quindi non abbiamo accesso alle chiamate di dispacciamento.

// create a group
dispatch_group_t group = dispatch_group_create();

// pair a dispatch_group_enter for each dispatch_group_leave
dispatch_group_enter(group);     // pair 1 enter
[self computeInBackground:1 completion:^{
    NSLog(@"1 done");
    dispatch_group_leave(group); // pair 1 leave
}];

// again... (and again...)
dispatch_group_enter(group);     // pair 2 enter
[self computeInBackground:2 completion:^{
    NSLog(@"2 done");
    dispatch_group_leave(group); // pair 2 leave
}];

// Next, setup the code to execute after all the paired enter/leave calls.
//
// Option 1: Get a notification on a block that will be scheduled on the specified queue:
dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
    NSLog(@"finally!");
});

// Option 2: Block an wait for the calls to complete in code already running
// (as cbartel points out, be careful with running this on the main/UI queue!):
//
// dispatch_group_wait(group, DISPATCH_TIME_FOREVER); // blocks current thread
// NSLog(@"finally!");

In questo esempio, computeInBackground: completamento: è implementato come:

- (void)computeInBackground:(int)no completion:(void (^)(void))block {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
        NSLog(@"%d starting", no);
        sleep(no*2);
        block();
    });
}

Output (con timestamp di una corsa):

12:57:02.574  2 starting
12:57:02.574  1 starting
12:57:04.590  1 done
12:57:06.590  2 done
12:57:06.591  finally!

1
@ ɲeuroburɳ Il codice sopra riportato attende sul thread principale. Credo che questo bloccherà il thread principale e causerà la mancata risposta dell'interfaccia utente fino al completamento dell'intero gruppo. Consiglio di spostare l'attesa su un thread in background. Ad esempio, dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_HIGH, 0)
cbartel,

2
@cbartel, buona cattura! Ho aggiornato il codice di esempio per riflettere il tuo commento. Molte volte è necessario che il callback sia nella coda principale, in questo caso dispatch_queue_notifyè probabilmente meglio (a meno che il tempo di blocco non sia garantito per essere breve).
ɲeuroburɳ

Dove posso rilasciare il gruppo (ad esempio dispatch_release (gruppo))? Non sono sicuro che sia sicuro rilasciare in dispatch_group_notify. Ma poiché questo è il codice che viene eseguito dopo il completamento del gruppo, non sono sicuro di dove rilasciare.
GingerBreadMane,

Se si utilizza ARC quindi non è necessario chiamare dispatch_release: stackoverflow.com/questions/8618632/...
ɲeuroburɳ

3
Bel post che spiega ulteriormente che: commandhift.co.uk/blog/2014/03/19/…
Rizon

98

Con Swift 5.1, Grand Central Dispatch offre molti modi per risolvere il tuo problema. In base alle tue esigenze, puoi scegliere uno dei sette motivi mostrati nei seguenti frammenti del parco giochi.


# 1. Usando DispatchGroup, DispatchGroup' notify(qos:flags:queue:execute:)e DispatchQueue'async(group:qos:flags:execute:)

La Guida alla programmazione della concorrenza per gli sviluppatori Apple affermaDispatchGroup :

I gruppi di invio sono un modo per bloccare un thread fino al completamento dell'esecuzione di una o più attività. È possibile utilizzare questo comportamento in luoghi in cui non è possibile progredire fino al completamento di tutte le attività specificate. Ad esempio, dopo aver inviato diverse attività per calcolare alcuni dati, è possibile utilizzare un gruppo per attendere tali attività e quindi elaborare i risultati al termine.

import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

let queue = DispatchQueue(label: "com.company.app.queue", attributes: .concurrent)
let group = DispatchGroup()

queue.async(group: group) {
    print("#1 started")
    Thread.sleep(forTimeInterval: 5)
    print("#1 finished")
}

queue.async(group: group) {
    print("#2 started")
    Thread.sleep(forTimeInterval: 2)
    print("#2 finished")
}

group.notify(queue: queue) {
    print("#3 finished")
}

/*
 prints:
 #1 started
 #2 started
 #2 finished
 #1 finished
 #3 finished
 */

# 2. Utilizzando DispatchGroup, DispatchGroup's wait(), DispatchGroup' s enter()e DispatchGroup'sleave()

import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

let queue = DispatchQueue(label: "com.company.app.queue", attributes: .concurrent)
let group = DispatchGroup()

group.enter()
queue.async {
    print("#1 started")
    Thread.sleep(forTimeInterval: 5)
    print("#1 finished")
    group.leave()
}

group.enter()
queue.async {
    print("#2 started")
    Thread.sleep(forTimeInterval: 2)
    print("#2 finished")
    group.leave()
}

queue.async {
    group.wait()
    print("#3 finished")
}

/*
 prints:
 #1 started
 #2 started
 #2 finished
 #1 finished
 #3 finished
 */

Nota che si può anche mescolare DispatchGroup wait()con DispatchQueue async(group:qos:flags:execute:)o mescolare DispatchGroup enter()e DispatchGroup leave()con DispatchGroup notify(qos:flags:queue:execute:).


# 3. Usando e 'sDispatch​Work​Item​Flags barrierDispatchQueueasync(group:qos:flags:execute:)

Grand Central Dispatch Tutorial per Swift 4: l' articolo 1/2 di Raywenderlich.com fornisce una definizione per le barriere :

Le barriere di invio sono un gruppo di funzioni che agiscono come un collo di bottiglia in stile seriale quando si lavora con code simultanee. Quando si invia un DispatchWorkItema una coda di invio, è possibile impostare flag per indicare che dovrebbe essere l'unico elemento eseguito sulla coda specificata per quel particolare momento. Ciò significa che tutti gli articoli inviati alla coda prima della barriera di spedizione devono essere completati prima DispatchWorkItemdell'esecuzione.

Uso:

import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

let queue = DispatchQueue(label: "com.company.app.queue", attributes: .concurrent)

queue.async {
    print("#1 started")
    Thread.sleep(forTimeInterval: 5)
    print("#1 finished")
}

queue.async {
    print("#2 started")
    Thread.sleep(forTimeInterval: 2)
    print("#2 finished")
}

queue.async(flags: .barrier) {
    print("#3 finished")
}

/*
 prints:
 #1 started
 #2 started
 #2 finished
 #1 finished
 #3 finished
 */

# 4. Usando DispatchWorkItem, Dispatch​Work​Item​Flags' barriere DispatchQueue'async(execute:)

import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

let queue = DispatchQueue(label: "com.company.app.queue", attributes: .concurrent)

queue.async {
    print("#1 started")
    Thread.sleep(forTimeInterval: 5)
    print("#1 finished")
}

queue.async {
    print("#2 started")
    Thread.sleep(forTimeInterval: 2)
    print("#2 finished")
}

let dispatchWorkItem = DispatchWorkItem(qos: .default, flags: .barrier) {
    print("#3 finished")
}

queue.async(execute: dispatchWorkItem)

/*
 prints:
 #1 started
 #2 started
 #2 finished
 #1 finished
 #3 finished
 */

# 5. Usando DispatchSemaphore, DispatchSemaphore' wait()e DispatchSemaphore'signal()

Soroush Khanlou ha scritto le seguenti righe nel post del blog The GCD Handbook :

Usando un semaforo, possiamo bloccare un thread per un periodo di tempo arbitrario, fino a quando non viene inviato un segnale da un altro thread. I semafori, come il resto di GCD, sono thread-safe e possono essere attivati ​​da qualsiasi luogo. I semafori possono essere usati quando c'è un'API asincrona che devi rendere sincrona, ma non puoi modificarla.

Riferimento API API per sviluppatori fornisce anche la seguente discussione per l' DispatchSemaphore init(value:​)inizializzatore:

Il passaggio zero per il valore è utile quando due thread devono riconciliare il completamento di un evento specifico. Il passaggio di un valore maggiore di zero è utile per la gestione di un pool di risorse finito, in cui la dimensione del pool è uguale al valore.

Uso:

import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

let queue = DispatchQueue(label: "com.company.app.queue", attributes: .concurrent)
let semaphore = DispatchSemaphore(value: 0)

queue.async {
    print("#1 started")
    Thread.sleep(forTimeInterval: 5)
    print("#1 finished")
    semaphore.signal()
}

queue.async {
    print("#2 started")
    Thread.sleep(forTimeInterval: 2)
    print("#2 finished")
    semaphore.signal()
}

queue.async {
    semaphore.wait()
    semaphore.wait()    
    print("#3 finished")
}

/*
 prints:
 #1 started
 #2 started
 #2 finished
 #1 finished
 #3 finished
 */

# 6. Usando OperationQueuee Operation'saddDependency(_:)

Il riferimento all'API per gli sviluppatori Apple indica Operation​Queue:

Le code delle operazioni utilizzano la libdispatchlibreria (nota anche come Grand Central Dispatch) per avviare l'esecuzione delle loro operazioni.

Uso:

import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

let operationQueue = OperationQueue()

let blockOne = BlockOperation {
    print("#1 started")
    Thread.sleep(forTimeInterval: 5)
    print("#1 finished")
}

let blockTwo = BlockOperation {
    print("#2 started")
    Thread.sleep(forTimeInterval: 2)
    print("#2 finished")
}

let blockThree = BlockOperation {
    print("#3 finished")
}

blockThree.addDependency(blockOne)
blockThree.addDependency(blockTwo)

operationQueue.addOperations([blockThree, blockTwo, blockOne], waitUntilFinished: false)

/*
 prints:
 #1 started
 #2 started
 #2 finished
 #1 finished
 #3 finished
 or
 #2 started
 #1 started
 #2 finished
 #1 finished
 #3 finished
 */

# 7. Uso di OperationQueuee OperationQueue's addBarrierBlock(_:)(richiede iOS 13)

import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

let operationQueue = OperationQueue()

let blockOne = BlockOperation {
    print("#1 started")
    Thread.sleep(forTimeInterval: 5)
    print("#1 finished")
}

let blockTwo = BlockOperation {
    print("#2 started")
    Thread.sleep(forTimeInterval: 2)
    print("#2 finished")
}

operationQueue.addOperations([blockTwo, blockOne], waitUntilFinished: false)
operationQueue.addBarrierBlock {
    print("#3 finished")
}

/*
 prints:
 #1 started
 #2 started
 #2 finished
 #1 finished
 #3 finished
 or
 #2 started
 #1 started
 #2 finished
 #1 finished
 #3 finished
 */

Esiste una soluzione per le chiamate asincrone senza utilizzare group.enter () e group.leave () per ciascuna (e senza semafori)? Come se avessi bisogno di attendere una richiesta asincrona a un server, quindi dopo attendere una seconda richiesta asincrona e così via. Ho letto questo articolo avanderlee.com/swift/asynchronous-operations ma non ne vedo un semplice utilizzo rispetto a BlockOperation
Woof

58

Un'altra alternativa GCD è una barriera:

dispatch_queue_t queue = dispatch_queue_create("com.company.app.queue", DISPATCH_QUEUE_CONCURRENT);

dispatch_async(queue, ^{ 
    NSLog(@"start one!\n");  
    sleep(4);  
    NSLog(@"end one!\n");
});

dispatch_async(queue, ^{  
    NSLog(@"start two!\n");  
    sleep(2);  
    NSLog(@"end two!\n"); 
});

dispatch_barrier_async(queue, ^{  
    NSLog(@"Hi, I'm the final block!\n");  
});

Basta creare una coda simultanea, inviare i due blocchi e quindi inviare il blocco finale con barriera, il che farà aspettare che finiscano gli altri due.


C'è qualche problema se non ho usato sleep (4);
Himanth,

No, ovviamente, non c'è nessun problema. In realtà, praticamente non lo vorrai mai sleep()! Ho aggiunto quelle sleep()chiamate solo per motivi pedagogici, per far funzionare i blocchi abbastanza a lungo in modo da poter vedere che vengono eseguiti contemporaneamente. In questo banale esempio, in assenza di sleep()questi due blocchi potrebbero essere eseguiti così rapidamente che il blocco inviato potrebbe iniziare e finire prima che si abbia la possibilità di osservare empiricamente l'esecuzione simultanea. Ma non sleep()nel tuo codice.
Rob,

39

So che hai chiesto informazioni su GCD, ma se lo desideri, NSOperationQueuegestisce anche questo tipo di cose in modo molto elegante, ad esempio:

NSOperationQueue *queue = [[NSOperationQueue alloc] init];

NSOperation *completionOperation = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"Starting 3");
}];

NSOperation *operation;

operation = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"Starting 1");
    sleep(7);
    NSLog(@"Finishing 1");
}];

[completionOperation addDependency:operation];
[queue addOperation:operation];

operation = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"Starting 2");
    sleep(5);
    NSLog(@"Finishing 2");
}];

[completionOperation addDependency:operation];
[queue addOperation:operation];

[queue addOperation:completionOperation];

3
Questo va bene quando il codice all'interno di NSBlockOperation è sincrono. Ma cosa succede se non lo è, e si desidera attivare il completamento al termine dell'operazione asincrona?
Greg Maletic,

3
@GregMaletic In tal caso, creo una NSOperationsottoclasse simultanea e impostata isFinishedal termine del processo asincrono. Quindi le dipendenze funzionano bene.
Rob,


1
@GregMaletic Sì, puoi usarlo anche (purché dispatch_semaphore_waitnon si stia svolgendo sulla coda principale e fintanto che i segnali e le attese sono bilanciati). Fintanto che non blocchi la coda principale, un approccio a semaforo va bene, se non hai bisogno della flessibilità delle operazioni (ad esempio avere la possibilità di annullarle, la capacità di controllare il grado di concorrenza, ecc.).
Rob,

1
@ Reza.Ab: se è necessario completare l'attività uno prima dell'avvio dell'attività due, aggiungere una dipendenza tra tali attività. O se la coda esegue sempre solo un'attività alla volta, impostarla maxConcurrentOperationCountcome coda seriale impostando su 1. È possibile impostare anche la priorità delle operazioni, entrambe qualityOfServicee queuePriority, ma queste hanno un impatto molto più sottile sulla priorità dell'attività rispetto alle dipendenze e / o al grado di concorrenza delle code.
Rob,

4

Le risposte sopra sono tutte fantastiche, ma hanno perso una cosa. group esegue le attività (blocchi) nel thread in cui è entrato quando si utilizza dispatch_group_enter/ dispatch_group_leave.

- (IBAction)buttonAction:(id)sender {
      dispatch_queue_t demoQueue = dispatch_queue_create("com.demo.group", DISPATCH_QUEUE_CONCURRENT);
      dispatch_async(demoQueue, ^{
        dispatch_group_t demoGroup = dispatch_group_create();
        for(int i = 0; i < 10; i++) {
          dispatch_group_enter(demoGroup);
          [self testMethod:i
                     block:^{
                       dispatch_group_leave(demoGroup);
                     }];
        }

        dispatch_group_notify(demoGroup, dispatch_get_main_queue(), ^{
          NSLog(@"All group tasks are done!");
        });
      });
    }

    - (void)testMethod:(NSInteger)index block:(void(^)(void))completeBlock {
      NSLog(@"Group task started...%ld", index);
      NSLog(@"Current thread is %@ thread", [NSThread isMainThread] ? @"main" : @"not main");
      [NSThread sleepForTimeInterval:1.f];

      if(completeBlock) {
        completeBlock();
      }
    }

questo viene eseguito nella coda concorrente creata demoQueue. Se non creo alcuna coda, viene eseguito nel thread principale .

- (IBAction)buttonAction:(id)sender {
    dispatch_group_t demoGroup = dispatch_group_create();
    for(int i = 0; i < 10; i++) {
      dispatch_group_enter(demoGroup);
      [self testMethod:i
                 block:^{
                   dispatch_group_leave(demoGroup);
                 }];
    }

    dispatch_group_notify(demoGroup, dispatch_get_main_queue(), ^{
      NSLog(@"All group tasks are done!");
    });
    }

    - (void)testMethod:(NSInteger)index block:(void(^)(void))completeBlock {
      NSLog(@"Group task started...%ld", index);
      NSLog(@"Current thread is %@ thread", [NSThread isMainThread] ? @"main" : @"not main");
      [NSThread sleepForTimeInterval:1.f];

      if(completeBlock) {
        completeBlock();
      }
    }

e c'è un terzo modo per rendere le attività eseguite in un altro thread:

- (IBAction)buttonAction:(id)sender {
      dispatch_queue_t demoQueue = dispatch_queue_create("com.demo.group", DISPATCH_QUEUE_CONCURRENT);
      //  dispatch_async(demoQueue, ^{
      __weak ViewController* weakSelf = self;
      dispatch_group_t demoGroup = dispatch_group_create();
      for(int i = 0; i < 10; i++) {
        dispatch_group_enter(demoGroup);
        dispatch_async(demoQueue, ^{
          [weakSelf testMethod:i
                         block:^{
                           dispatch_group_leave(demoGroup);
                         }];
        });
      }

      dispatch_group_notify(demoGroup, dispatch_get_main_queue(), ^{
        NSLog(@"All group tasks are done!");
      });
      //  });
    }

Naturalmente, come detto, puoi usare dispatch_group_asyncper ottenere ciò che desideri.


3

La prima risposta è sostanzialmente corretta, ma se vuoi il modo più semplice per ottenere il risultato desiderato, ecco un esempio di codice autonomo che dimostra come farlo con un semaforo (che è anche il modo in cui i gruppi di dispacciamento lavorano dietro le quinte, JFYI) :

#include <dispatch/dispatch.h>
#include <stdio.h>

main()
{
        dispatch_queue_t myQ = dispatch_queue_create("my.conQ", DISPATCH_QUEUE_CONCURRENT);
        dispatch_semaphore_t mySem = dispatch_semaphore_create(0);

        dispatch_async(myQ, ^{ printf("Hi I'm block one!\n"); sleep(2); dispatch_semaphore_signal(mySem);});
        dispatch_async(myQ, ^{ printf("Hi I'm block two!\n"); sleep(4); dispatch_semaphore_signal(mySem);});
        dispatch_async(myQ, ^{ dispatch_semaphore_wait(mySem, DISPATCH_TIME_FOREVER); printf("Hi, I'm the final block!\n"); });
        dispatch_main();
}

7
Due osservazioni: 1. Ti manca a dispatch_semaphore_wait. Hai due segnali, quindi hai bisogno di due attese. Così com'è, il tuo blocco "completamento" inizierà non appena il primo blocco segnala il semaforo, ma prima che l'altro blocco finisca; 2. Dato che questa era una domanda iOS, scoraggerei l'uso di dispatch_main.
Rob,

1
Sono d'accordo con Rob. Questa non è una soluzione valida. Lo dispatch_semaphore_waitsbloccherà non appena dispatch_semaphore_signalviene chiamato uno dei metodi. La ragione per cui questo può sembrare funzionare è che i printfblocchi "uno" e "due" si verificano immediatamente e printfil "finalmente" si verifica dopo un'attesa, quindi dopo che il blocco ha dormito per 2 secondi. Se inserisci la stampa dopo le sleepchiamate, otterrai l'output per "uno", quindi 2 secondi dopo per "finalmente", quindi 2 secondi dopo per "due".
ɲeuroburɳ

1

Risposta accettata in breve tempo:

let group = DispatchGroup()

group.async(group: DispatchQueue.global(qos: .default), execute: {
    // block1
    print("Block1")
    Thread.sleep(forTimeInterval: 5.0)
    print("Block1 End")
})


group.async(group: DispatchQueue.global(qos: .default), execute: {
    // block2
    print("Block2")
    Thread.sleep(forTimeInterval: 8.0)
    print("Block2 End")
})

dispatch_group_notify(group, DispatchQueue.global(qos: .default), {
    // block3
    print("Block3")
})

// only for non-ARC projects, handled automatically in ARC-enabled projects.
dispatch_release(group)

0

Esempio di Swift 4.2:

let group = DispatchGroup.group(count: 2)
group.notify(queue: DispatchQueue.main) {
     self.renderingLine = false
     // all groups are done
}
DispatchQueue.main.async {
    self.renderTargetNode(floorPosition: targetPosition, animated: closedContour) {
        group.leave()
        // first done
    }
    self.renderCenterLine(position: targetPosition, animated: closedContour) {
        group.leave()
        // second done
    }
 }

group.leave()causato incidente
Ben

-3

Per non dire che altre risposte non sono grandi per determinate circostanze, ma questo è un frammento che utilizzo sempre da Google:

- (void)runSigninThenInvokeSelector:(SEL)signInDoneSel {


    if (signInDoneSel) {
        [self performSelector:signInDoneSel];
    }

}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.