dispatch_async and calling a completion handler on the original queue

后端 未结 4 1751
忘掉有多难
忘掉有多难 2020-12-08 10:26

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 completi

相关标签:
4条回答
  • 2020-12-08 10:47

    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.

    0 讨论(0)
  • 2020-12-08 10:55

    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
    });
    }
    
    0 讨论(0)
  • 2020-12-08 11:11

    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);
        }];
    }];
    
    0 讨论(0)
  • 2020-12-08 11:13

    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.

    0 讨论(0)
提交回复
热议问题