web-dev-qa-db-fra.com

dispatch_async et appeler un gestionnaire d'achèvement dans la file d'attente d'origine

J'ai vu des questions connexes, mais aucune ne semble répondre à ce cas. Je veux écrire une méthode qui fera un peu de travail en arrière-plan. J'ai besoin de cette méthode pour appeler un rappel de fin sur le même thread/file d'attente utilisé pour l'appel de méthode d'origine.

- (void)someMethod:(void (^)(BOOL result))completionHandler {
    dispatch_queue_t current_queue = // ???

    // some setup code here
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        BOOL ok = // some result

        // do some long running processing here

        dispatch_async(current_queue, ^{
            completionHandler(ok);
        });
    });

Quelle incantation magique est nécessaire ici pour que le gestionnaire de complétion soit appelé sur la même file d'attente ou thread que l'appel à sameMethod? Je ne veux pas assumer le fil principal. Et bien sûr dispatch_get_current_queue ne doit pas être utilisé.

25
rmaddy

Si vous regardez dans les documents Apple Apple, il semble y avoir deux modèles.

S'il est supposé que le gestionnaire d'achèvement doit être exécuté sur le thread principal, aucune file d'attente ne doit être fournie. Un exemple serait les méthodes UIView de animations:

+ (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion

Sinon, l'API demande généralement à l'appelant de fournir une file d'attente:

[foo doSomethingWithCompletion:completion targetQueue:yourQueue];

Ma suggestion est de suivre ce modèle. S'il n'est pas clair quelle file d'attente le gestionnaire d'achèvement doit être appelé, l'appelant doit le fournir explicitement en tant que paramètre.

13
D.C.

Vous ne pouvez pas vraiment utiliser les files d'attente pour cela, car, à part la file d'attente principale, aucune d'entre elles ne peut être exécutée sur un thread particulier. Au lieu de cela, vous devrez récupérer le thread et y exécuter votre bloc directement.

Adaptation de Mike Ash's Block Additions :

// The last public superclass of Blocks is NSObject
@implementation NSObject (rmaddy_CompletionHandler)

- (void)rmaddy_callBlockWithBOOL: (NSNumber *)b
{
    BOOL ok = [b boolValue];
    void (^completionHandler)(BOOL result) = (id)self;
    completionHandler(ok);
}

@end

- (void)someMethod:(void (^)(BOOL result))completionHandler {
    NSThread * origThread = [NSThread currentThread];

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        BOOL ok = // some result

        // do some long running processing here

        // Check that there was not a nil handler passed.
        if( completionHandler ){
            // This assumes ARC. If no ARC, copy and autorelease the Block.
            [completionHandler performSelector:@selector(rmaddy_callBlockWithBOOL:)
                                      onThread:origThread
                                    withObject:@(ok)    // or [NSNumber numberWithBool:ok]
                                 waitUntilDone:NO];
        }
        });
    });

Bien que vous n'utilisiez pas dispatch_async(), ceci est toujours asynchrone par rapport au reste de votre programme, car il est contenu dans le bloc de tâches distribué d'origine, et waitUntilDone:NO Le rend également asynchrone avec respect pour que.

6
jscs

vous ne savez pas si cela résoudra le problème, mais que diriez-vous d'utiliser NSOperations au lieu de GCD?:

- (void)someMethod:(void (^)(BOOL result))completionHandler {
NSOperationQueue *current_queue = [NSOperationQueue currentQueue];

// some setup code here
NSOperationQueue *q = [[NSOperationQueue alloc] init];
[q addOperationWithBlock:^{
    BOOL ok = YES;// some result

    // do some long running processing here
    [current_queue addOperationWithBlock:^{
        completionHandler(ok);
    }];
}];
3
Arian Sharifian

Je voulais faire quelques tâches sur une file d'attente puis exécuter un bloc d'achèvement comme @rmaddy l'a mentionné. Je suis tombé sur le guide de programmation d'accès concurrentiel de Apple et l'ai implémenté (avec dispatch_retain & dispatch_released commenté parce que j'utilise ARC) - https://developer.Apple.com/ library/ios/documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html # // Apple_ref/doc/uid/TP40008091-CH102-SW1

void average_async(int *data, size_t len, dispatch_queue_t queue, void (^block)(int))
{
// Retain the queue provided by the user to make
// sure it does not disappear before the completion
// block can be called.
//dispatch_retain(queue); // comment out if use ARC

// Do the work on user-provided queue
dispatch_async(queue, ^{
  int avg = average(data, len);
  dispatch_async(queue, ^{ block(avg);});

  // Release the user-provided queue when done
  //dispatch_release(queue); // comment out if use ARC
});
}
0
Loozie