2012-10-27 30 views
26

我已经看到一些相关的问题,但似乎没有人回答这种情况。我想写一个方法,将在后台做一些工作。我需要这种方法来调用与原始方法调用相同的线程/队列的完成回调。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不会被使用。

+0

你能描述你想要达到什么样的?为什么你的特定目的对它执行的线程有影响? –

+0

@ChristopherPickslay'someMethod'可能会在某个后台线程中调用。我希望它可以在同一个线程上调用完成块,而不是主线程或其他任意后台线程。 – rmaddy

+0

我明白这一点。问题是为什么。是否有某些技术原因需要在特定线程上调用?我只是想,可能会有不同的设计,这将有所帮助。 –

回答

4

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

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

虽然你不使用dispatch_async(),这仍是异步相对于你的程序的其余部分,因为它包含了原派遣任务中块,并waitUntilDone:NO也使它异步就此。

+0

这是一个有趣的想法。但是这有一个严重的缺点。对于我想要以这种方式使用的每个可能的块签名,我将不得不添加相应的类别方法。 – rmaddy

+0

**“除了主队列之外,它们都不能保证在任何特定的线程上运行”** - 您能否指出我在苹果文档中提到的这个地方? – Macondo2Seattle

+0

@BlackRider:https://developer.apple.com/library/mac/documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html,位于“调度队列任务”标题下。 –

11

如果您查看Apple文档,看起来有两种模式。

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

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

否则,API通常会询问主叫方提供一个队列:

[foo doSomethingWithCompletion:completion targetQueue:yourQueue]; 

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

+0

查看'UIDocument saveToURL:forSaveOperation:completionHandler:'的文档。完成处理程序状态的描述*在调用队列中调用此块。这是我希望达到的。 – rmaddy

+0

+1创建您自己的串行队列并运行该队列上的方法和完成块。 – Abizern

+0

我认为这是UIDocument中的一个错误。它不应该假定始发队列具有它想要的行为。 –

2

不知道这是否就能解决问题,但关于使用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); 
    }]; 
}]; 
+1

[NSOperationQueue currentQueue]可能会返回零。 “从运行操作的上下文外调用此方法通常会导致返回零。” – BB9z

0

我想要做一些任务上的一些队列,然后执行完成块作为@rmaddy提到如何。我遇到了并发编程指南,从苹果和实施这个(与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 
}); 
} 
+0

你用错误的方式引用了答案:)第一个异步是在全局队列中。在该队列上完成工作后,该块将在传入的队列上执行 – hariszaman

相关问题