我看过一些相关问题,但似乎没有人回答这个问题。我想写一个在后台做一些工作的方法。我需要这个方法在用于原始方法调用的同一个线程/队列上调用完成回调。
- (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 :(得分:12)
如果查看Apple文档,似乎有两种模式。
如果假设要在主线程上运行完成处理程序,则不需要提供队列。一个例子是UIView
的{{1}}方法:
animations
否则,API通常会要求调用者提供队列:
+ (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion
我的建议是遵循这种模式。如果不清楚应该调用完成处理程序的队列,则调用者应该明确地将其作为参数提供。
答案 1 :(得分:5)
你不能真正使用队列,因为除了主队列之外,它们都不能保证在任何特定线程上运行。相反,你必须直接获取线程并执行你的块。
改编自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
也使它成为异步的那个。
答案 2 :(得分: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);
}];
}];
答案 3 :(得分:0)
我想在某个队列上执行某些任务,然后执行@rmaddy提到的完成块。我遇到了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
});
}