我编写了一个黑盒类,使用 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()
在主线程上调用其延续 block 。参见 Invoke model method with block that will run on the main thread举个例子。
然而,这种常见情况假设客户端是从主线程调用的。我想更笼统一点,在调用者的线程上调用延续 block ,它可能是也可能不是主线程。例如,这将允许与 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];
});
}
我想这可行,我打算试试。但是有什么不那么做作的吗?或许是用于 block 的 performSelector:onThread:
版本?
PS:为了清楚起见,我在上面的代码片段中省略了所有内存管理调用。例如,followup
block 是基于堆栈的,必须复制到堆中才能在另一个线程上使用它...
编辑:我发现 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;
最佳答案
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 并在之后释放它。我想你不会。
希望对你有帮助!
编辑:添加保留/释放
关于带有 block 的 Objective-C 调度方法将在 *caller* 线程上运行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7361384/