dispatch_async并调用原始队列上的完成处理程序

时间:2022-05-15 20:59:30

I've seen some related questions but none seem to answer this case. I want to write a method that will do some work in the background. I need this method to call a completion callback on the same thread / queue used for the original method call.

我见过一些相关的问题,但似乎没有一个能回答这个问题。我想写一个方法,在后台做一些工作。我需要这个方法来调用与原始方法调用相同的线程/队列的补全回调。

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

What magic incantation is needed here so the completion handler is called on the same queue or thread as the call to sameMethod? I don't want to assume the main thread. And of course dispatch_get_current_queue is not to be used.

这里需要什么神奇的咒语,以便在调用sameMethod的相同队列或线程上调用完成处理程序?我不想假设主线。当然,dispatch_get_current_queue是不使用的。

4 个解决方案

#1


11  

If you look through the Apple docs, there appear to be two patterns.

如果你浏览一下苹果文档,就会发现有两种模式。

If it is assumed that the completion handler is to be run on the main thread, then no queue needs to be provided. An example would be UIView's animations methods:

如果假定完成处理程序在主线程上运行,则不需要提供队列。UIView的动画方法就是一个例子:

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

Otherwise, the API usually asks the caller to provide a queue:

否则,API通常要求调用方提供队列:

[foo doSomethingWithCompletion:completion targetQueue:yourQueue];

My suggestion is to follow this pattern. If it is unclear which queue the completion handler should be called, the caller should supply it explicitly as a parameter.

我的建议是遵循这种模式。如果不清楚应该调用哪个队列完成处理程序,调用者应该显式地将其作为参数提供。

#2


5  

You can't really use queues for this because, aside from the main queue, none of them are guaranteed to be running on any particular thread. Instead, you will have to get the thread and execute your block directly there.

您不能为此真正地使用队列,因为除了主队列之外,它们都不能保证在任何特定的线程上运行。相反,您将必须获得线程并直接在那里执行您的块。

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

Although you're not using dispatch_async(), this is still asynchronous with respect to the rest of your program, because it's contained within the original dispatched task block, and waitUntilDone:NO also makes it asynchronous with respect to that.

尽管您没有使用dispatch_async(),但对于程序的其余部分来说,这仍然是异步的,因为它包含在原始分派的任务块中,而waitUntilDone:NO也使它相对于该任务块而言是异步的。

#3


3  

not sure if this will solve the problem, but how about using NSOperations instead of GCD?:

不确定这是否能解决问题,但是用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


0  

I wanted to do some tasks on some queue and then execute a completion block as @rmaddy mentioned. I came across the Concurrency Programming Guide from Apple and implemented this (with dispatch_retain & dispatch_released commented out because I am using ARC) -- https://developer.apple.com/library/ios/documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html#//apple_ref/doc/uid/TP40008091-CH102-SW1

我想在一些队列上执行一些任务,然后执行@rmaddy提到的完成块。我遇到了苹果公司的并发编程指南,并实现了它(使用dispatch_retain & dispatch_release,因为我使用的是ARC)——https://developer.apple.com/library/ios/documentation/general/conceptual/concurrencyprogrammingguide/operationqueues/operationqueues/operes.html #/ apple_ref/doc/ swid / t4000280000.1

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

#1


11  

If you look through the Apple docs, there appear to be two patterns.

如果你浏览一下苹果文档,就会发现有两种模式。

If it is assumed that the completion handler is to be run on the main thread, then no queue needs to be provided. An example would be UIView's animations methods:

如果假定完成处理程序在主线程上运行,则不需要提供队列。UIView的动画方法就是一个例子:

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

Otherwise, the API usually asks the caller to provide a queue:

否则,API通常要求调用方提供队列:

[foo doSomethingWithCompletion:completion targetQueue:yourQueue];

My suggestion is to follow this pattern. If it is unclear which queue the completion handler should be called, the caller should supply it explicitly as a parameter.

我的建议是遵循这种模式。如果不清楚应该调用哪个队列完成处理程序,调用者应该显式地将其作为参数提供。

#2


5  

You can't really use queues for this because, aside from the main queue, none of them are guaranteed to be running on any particular thread. Instead, you will have to get the thread and execute your block directly there.

您不能为此真正地使用队列,因为除了主队列之外,它们都不能保证在任何特定的线程上运行。相反,您将必须获得线程并直接在那里执行您的块。

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

Although you're not using dispatch_async(), this is still asynchronous with respect to the rest of your program, because it's contained within the original dispatched task block, and waitUntilDone:NO also makes it asynchronous with respect to that.

尽管您没有使用dispatch_async(),但对于程序的其余部分来说,这仍然是异步的,因为它包含在原始分派的任务块中,而waitUntilDone:NO也使它相对于该任务块而言是异步的。

#3


3  

not sure if this will solve the problem, but how about using NSOperations instead of GCD?:

不确定这是否能解决问题,但是用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


0  

I wanted to do some tasks on some queue and then execute a completion block as @rmaddy mentioned. I came across the Concurrency Programming Guide from Apple and implemented this (with dispatch_retain & dispatch_released commented out because I am using ARC) -- https://developer.apple.com/library/ios/documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html#//apple_ref/doc/uid/TP40008091-CH102-SW1

我想在一些队列上执行一些任务,然后执行@rmaddy提到的完成块。我遇到了苹果公司的并发编程指南,并实现了它(使用dispatch_retain & dispatch_release,因为我使用的是ARC)——https://developer.apple.com/library/ios/documentation/general/conceptual/concurrencyprogrammingguide/operationqueues/operationqueues/operes.html #/ apple_ref/doc/ swid / t4000280000.1

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