dispatch_async and calling a completion handler on the original queue

荒凉一梦 提交于 2019-11-28 05:52:17
D.C.

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:

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

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

[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.

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.

not sure if this will solve the problem, but how about using NSOperations instead of 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);
    }];
}];

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

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
});
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!