我写了一个黑盒子类,使用Grand Central Dispatch在后台进行大量处理。我打算提供一个延续式API,如:
- (void) processHeavyStuff:(id) someParam thenDo:(ContinuationBlock)followup;
客户可以像这样打电话:
[myBlackBox processHeavyStuff:heavyOne thenDo: ^(Dalek* result){
[self updateDisplayWithNewDalek:result];
}];
通常做的是processHeavyStuff:thenDo:
实现使用dispatch_get_main_queue()
在主线程上调用其连续块。有关示例,请参阅Invoke model method with block that will run on the main thread。
然而,这种常见情况假设客户端正在从主线程调用。我想更通用,并在调用者的线程上调用continuation块,这可能是也可能不是主线程。例如,这将允许与NSManagedObjectContext
是线程本地的Core Data线程客户端一起使用。对此有好的模式吗?
使用–[NSObject performSelector:onThread:withObject:waitUntilDone:]
,我可以看到我可以定义辅助方法:
- (void) callContinuation:(ContinuationBlockWithNoArgument) followup
{
followup();
}
然后在调用者的线程上执行该选择器:
- (void) processHeavyStuff:(id) someParam thenDo:(ContinuationBlock)followup
{
NSSthread *callerThread = [NSThread currentThread];
dispatch_async(self.backgroundQueue, ^ {
Dalek *newDalek = [self actuallyDoTheHeavyProcessing:someParam];
[self performSelector:@selector(callContinuation:) onThread:callerThread
withObject: ^{
followup(newDalek);
}
waitUntilDone:NO];
});
}
我想这可行,我会尝试一下。但是有什么不那么做作的吗?也许是块performSelector:onThread:
的版本?
PS:为了清楚起见,我将所有内存管理调用从上述代码段中删除。例如,followup
块是基于堆栈的,必须复制到堆中才能在另一个线程上使用它...
编辑:我发现Mike Ash使用了非常类似的方法:
void RunOnThread(NSThread *thread, BOOL wait, BasicBlock block)
{
[[[block copy] autorelease] performSelector: @selector(my_callBlock) onThread: thread withObject: nil waitUntilDone: wait];
}
在my_callBlock
上的类别中定义了NSObject
:
@implementation NSObject (BlocksAdditions)
- (void)my_callBlock
{
void (^block)(void) = (id)self;
block();
}
@end;
答案 0 :(得分:4)
dispatch_get_current_queue()返回当前队列,该队列是在方法开头调用时的调用者队列。将您的代码更改为:
- (void)processHeavyStuff:(id)someParam thenDo:(ContinuationBlock)followup {
dispatch_queue_t callerQueue = dispatch_get_current_queue();
dispatch_retain(callerQueue);
dispatch_async(self.backgroundQueue, ^ {
Dalek *newDalek = [self actuallyDoTheHeavyProcessing:someParam];
dispatch_async(callerQueue, ^{
followUp(dalek);
dispatch_release(callerQueue);
});
});
}
我不太确定的一件事是你是否需要保留callerQueue并在之后释放它。我想你没有。
希望能帮到你!
编辑:添加了保留/释放
答案 1 :(得分:1)
您可以简单地在-processHeavyStuff:thenDo:
方法中添加另一个参数,指定您希望块运行的队列。这种方法更灵活,在NSNotificationCenter
的{{1}}中使用(尽管它使用-addObserverForName:object:queue:usingBlock:
,但原理是相同的)。只是不要忘记你应该保留并释放你传入的队列。
答案 2 :(得分:0)
这是我如何处理它。它很灵活,但不是很优雅。
由于问题仍然没有答案,也许你还在寻找答案?如果没有,你能否提供最适合你的东西?
// Continuation block is executed on an arbitrary thread.
// Caller can change context to specific queue/thread in
// the continuation block if necessary.
- (void) processHeavyStuff:(id) someParam thenDo:(ContinuationBlock)followup;
// Continuation block is executed on the given GCD queue
- (void) processHeavyStuff:(id) someParam thenOnQueue:(dispatch_queue_t)queue do:(ContinuationBlock)followup;
// Continuation block is executed on the given thread
- (void) processHeavyStuff:(id) someParam thenOnThread:(NSThread*)thread do:(ContinuationBlock)followup;
// Continuation block is executed on *this* thread
- (void) processHeavyStuff:(id) someParam thenDoOnThisThread:(ContinuationBlock)followup;
// Continuation block is executed on main thread
- (void) processHeavyStuff:(id) someParam thenDoOnMainThread:(ContinuationBlock)followup;