Objective-C等待异步操作并将完成处理程序排入队列

时间:2015-03-04 23:53:02

标签: ios objective-c grand-central-dispatch nsoperationqueue

我必须计算一个代价高昂的价值。计算此值后,我想运行完成处理程序块:

-(void) performCostlyCalculationWithCompletionHandler:(void (^)(void)complete 
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        id result = [self costlyCalculation];
        dispatch_async(dispatch_get_main_queue(), ^{
            complete(result);
        });
    });
}

非常标准。

现在,我希望能够重复调用此函数,而无需重新入列costlyCalculation。如果costlyCalculation已在运行,我只想保存完成块,并在result完成后使用相同的costlyCalculation全部调用它们。

使用GCD或NSOperationQueue有一种简单的方法吗?或者我应该将完成块存储在NSArray中并自己调用它们?如果我这样做,我需要在这个数组周围进行什么样的同步?

更新

我能够与dispatch_group_notify取得联系。基本上,我可以将工作块排队并将所有完成处理程序排入队列之后运行:

dispatch_group_t group = dispatch_group_create();

dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_group_async(group, queue, ^(){
    // Do something that takes a while
    id result = [self costlyCalculation];
    dispatch_group_async(group, dispatch_get_main_queue(), ^(){
        self.result = result;
    });
});

dispatch_group_notify(group, dispatch_get_main_queue(), ^(){
    complete(result);
});

这样可行,但如果我不需要,我怎么能判断costlyCalcuation是否已经在运行并且不会将工作排入队列?

1 个答案:

答案 0 :(得分:1)

我认为你已经解决了这个问题。我刚刚想出了一个使用NSOperationQueue和NSOperations之间依赖关系的替代方案。这是我认为的伪代码。

// somewhere, create operation queue
NSOperationQueue *opQueue = [[NSOperationQueue alloc] init];

-(void)tryCalculation:(CompletionBlockType)completionBlock
{
    if(opQueue.operationCount > 0)
    {
        NSOperation *op = [[NSOperation alloc] init];
        op.completionBlock = completionBlock;
        // you can control how to synchronize completion blocks by changing dependency object. In this example, all operation will be triggered at once when costly calculation finishes
        [op addDependency:[opQueue.operations firstObject]];
        [opQueue addOperation:op];
    }
    else
    {
        NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(costlyCalculation) object:nil];
        op.completionBlock = completionBlock;
        [opQueue addOperation:op];
    }
}

仍然存在微妙的时间问题。也许我们可以在昂贵的计算函数中使用额外的标志。