2012-10-27 12 views
26

Widziałem kilka powiązanych pytań, ale nie wydaje się, aby odpowiedzieć na tę sprawę. Chcę napisać metodę, która będzie działała w tle. Potrzebuję tej metody, aby wywołać wywołanie zwrotne zakończenia w tym samym wątku/kolejce, który był używany w pierwotnym wywołaniu metody.dispatch_async i wywoływanie procedury obsługi zakończenia w oryginalnej kolejce

- (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); 
     }); 
    }); 

Co magiczne zaklęcie konieczna jest tu więc handler zakończenie nazywa się w tej samej kolejce lub wątku jako wezwanie do sameMethod? Nie chcę zakładać głównego wątku. I oczywiście nie należy używać dispatch_get_current_queue.

+0

można opisać to, co próbujesz osiągnąć? Dlaczego ma znaczenie dla twoich szczególnych celów, w jaki wątek jest wykonywany? –

+0

@ChristopherPickslay 'someMethod' może zostać wywołany w wątku w tle. Chcę to tak, że blok zakończenia jest wywoływany w tym samym wątku, a nie główny wątek lub inny arbitralny wątek tła. – rmaddy

+0

Rozumiem to. Pytanie brzmi: dlaczego? Czy jest jakiś techniczny powód, dla którego należy go wywołać w konkretnym wątku? Po prostu myślę, że może istnieć inny projekt, który mógłby pomóc. –

Odpowiedz

4

Naprawdę nie można używać do tego kolejek, ponieważ poza główną kolejką, żadna z nich nie może działać na żadnym wątku. Zamiast tego będziesz musiał pobrać wątek i wykonać swój blok bezpośrednio tam.

Adaptacja z 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]; 
     } 
     }); 
    }); 

Chociaż nie używasz dispatch_async(), to wciąż asynchroniczny w stosunku do pozostałej części programu, ponieważ jest on zawarty w pierwotnym wysłane zadania blok, i waitUntilDone:NO również czyni to asynchronicznym w odniesieniu do tego.

+0

To jest ciekawy pomysł. Ale ma to poważny minus. Dla każdego możliwego podpisu bloku, który mógłbym chcieć użyć w ten sposób, musiałbym dodać odpowiednią metodę kategorii. – rmaddy

+0

** "oprócz głównej kolejki, żadna z nich nie może działać na żadnym konkretnym wątku" ** - czy mógłbyś wskazać mi miejsce w dokumentach Apple, które to mówi? – Macondo2Seattle

+0

@BlackRider: https://developer.apple.com/library/mac/documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html pod nagłówkiem "Kolejkowanie zadań do wysłania". –

11

Jeśli przejrzysz dokumentację Apple, wyglądają na dwa wzory.

Jeśli założono, że procedura obsługi zakończenia ma zostać uruchomiona w wątku głównym, nie trzeba podawać kolejki. Przykładem może być UIView „s animations metody:

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

przeciwnym razie API zwykle pyta rozmówcę, aby zapewnić kolejkę:

[foo doSomethingWithCompletion:completion targetQueue:yourQueue]; 

Moja sugestia jest, aby przestrzegać tego wzoru. Jeśli nie jest jasne, która kolejka powinna zostać wywołana procedura zakończenia, osoba wywołująca powinna podać ją jawnie jako parametr.

+0

Zobacz dokumenty dla 'UIDocument saveToURL: forSaveOperation: completionHandler:'. Opis stanu obsługi zakończenia * Ten blok jest wywoływany w kolejce wywołującej. *. Oto, co chcę osiągnąć. – rmaddy

+0

+1 Utwórz własną kolejkę szeregową i uruchom metodę i blok zakończenia tej kolejki. – Abizern

+0

Twierdzę, że jest to błąd w UIDocument. Nie należy zakładać, że kolejka wyjściowa ma pożądane zachowania. –

2

nie wiem, czy to rozwiąże problem, ale jak o użyciu NSOperations zamiast 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); 
    }]; 
}]; 
+1

[NSOperationQueue currentQueue] może zwrócić zero. "Wywołanie tej metody spoza kontekstu działającej operacji zazwyczaj powoduje zwrócenie wartości zerowej." – BB9z

0

Chciałem zrobić kilka zadań na jakiejś kolejce, a następnie wykonać blok zakończenia jako @rmaddy wymienione. Natknąłem Programming Współbieżnym Przewodnik od firmy Apple i wdrożone to (z dispatch_retain & dispatch_released wykomentowane ponieważ używam 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

zacytowałeś odpowiedź w niewłaściwy sposób :) pierwszy async znajduje się w globalnej kolejce lol. Po wykonaniu pracy w tej kolejce blok jest wykonywany w przekazywanej kolejce – hariszaman

Powiązane problemy