带有 block 的 Objective-C 调度方法将在 *caller* 线程上运行

标签 objective-c multithreading grand-central-dispatch objective-c-blocks

我编写了一个黑盒类,使用 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/

相关文章:

objective-c - 为什么 nsoperation 连续工作?

c# - 在创建对象的线程上调用方法

java - ArrayList(Collection<? extends E> c) 线程安全吗?

objective-c-blocks - iOS5 稳定应用程序在 iOS 6 中崩溃并出现 dealloc :objc_storeStrong:objc_release:_dispatch_xref_dispose

ios - 如何将 imageData 保存到 NSManagedObject 属性

macos - 如何减慢 GCD 并发队列的任务?

iOS:为什么我需要在 NSURLConnection sendAsynchronousRequest 之后使用 sleep(1)?

ios - Google VR View -设置视频类型

java - Java中的线程安全交换

objective-c - 有可以打开的文件吗?