Подтвердить что ты не робот

Dispatch_async и вызов обработчика завершения в исходной очереди

Я видел некоторые связанные вопросы, но никто, кажется, не ответил на это. Я хочу написать метод, который будет работать в фоновом режиме. Мне нужен этот метод для вызова обратного вызова завершения в том же потоке/очереди, что и для исходного вызова метода.

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

Какое волшебное заклинание необходимо здесь, чтобы обработчик завершения вызывался в той же очереди или потоке, что и вызов sameMethod? Я не хочу принимать основной поток. И, конечно, dispatch_get_current_queue не используется.

4b9b3361

Ответ 1

Если вы просматриваете документы Apple, то есть два шаблона.

Если предполагается, что обработчик завершения должен быть запущен в основном потоке, тогда очередь не должна предоставляться. Примером может служить метод UIView animations:

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

В противном случае API обычно запрашивает у вызывающего пользователя очередь:

[foo doSomethingWithCompletion:completion targetQueue:yourQueue];

Мое предложение - следовать этому шаблону. Если неясно, какая очередь должна быть вызвана обработчиком завершения, вызывающий должен указать его явно как параметр.

Ответ 2

Вы не можете использовать для этого очереди, потому что, помимо основной очереди, ни один из них не будет работать в каком-либо конкретном потоке. Вместо этого вам нужно будет получить поток и выполнить свой блок непосредственно там.

Адаптация из Добавления блока Mike Ash:

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

Хотя вы не используете dispatch_async(), это по-прежнему асинхронно по отношению к остальной части вашей программы, поскольку оно содержится в исходном рассылаемом блоке задач, а waitUntilDone:NO также делает его асинхронным по отношению к этому.

Ответ 3

не уверен, что это решит проблему, но как насчет использования NSOperations вместо 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);
    }];
}];

Ответ 4

Я хотел выполнить некоторые задачи в некоторой очереди, а затем выполнить блок завершения, упомянутый @rmaddy. Я наткнулся на Concurrency Руководство по программированию от Apple и реализовал это (с сообщением dispatch_retain и dispatch_released, потому что я использую 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
});
}