NSOperation依赖和completionBlock

时间:2015-12-21 20:10:23

标签: objective-c nsoperation nsoperationqueue

我们遇到一个关于NSOperationQueue的简单问题,这是一个简单的操作逻辑:

self.queue = [[NSOperationQueue alloc] init];

NSOperation *operationA = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"- Running operation A");
    [NSThread sleepForTimeInterval:1.2];
    NSLog(@"- Done operation A");
}];

NSOperation *operationB = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"- Running operation B");
    [NSThread sleepForTimeInterval:2];
    NSLog(@"- Done operation B");
}];

[operationA setCompletionBlock:^{
    NSLog(@"-- Completion Block A");
}];

[operationB setCompletionBlock:^{
    NSLog(@"-- Completion Block B");
}];

[operationB addDependency:operationA];
[self.queue addOperations:@[operationA, operationB] waitUntilFinished:NO];

这是最终输出

2015-12-21 14:59:57.463 SampleProject[18046:310901] - Running operation A
2015-12-21 14:59:58.664 SampleProject[18046:310901] - Done operation A
2015-12-21 14:59:58.664 SampleProject[18046:310900] - Running operation B
2015-12-21 14:59:58.664 SampleProject[18046:310904] -- Completion Block A
2015-12-21 15:00:00.736 SampleProject[18046:310900] - Done operation B
2015-12-21 15:00:00.736 SampleProject[18046:310904] -- Completion Block B

正如我们所看到的,操作B 操作A 的completionBlock之前执行。在我们的实际应用中,我们有许多操作A ,只有一个操作B 依赖于所有操作A 。但是我们遇到的问题是 operationB 是在最后一次操作A 的完成块被调用之前启动的,这通常会给操作B提供信息。

如何在所有操作A 的完成块之后执行操作B

3 个答案:

答案 0 :(得分:3)

正如您在测试中发现的那样,完成块不是“队列的一部分”,而是在“操作队列”之外(在另一个线程上)运行。因此,操作A的completionBlock将与操作B同时运行(ish)。

我建议您重构代码以删除所有完成块。

你说你正在使用completionBlocks将操作A的信息传递给B,有两种选择:给所有A(非弱)提供B引用,所以当B运行时它可以从所有A中选择结果。或者如果由于某种原因保持所有A的直到B运行是不可行的,那么重新创建你的completionBlock作为另一个NSOperation:

NSOperation *operationA = [NSBlockOperation blockOperationWithBlock:^{
    // do stuff
}];

NSOperation *operationATail = [NSBlockOperation blockOperationWithBlock:^{
    // do completionBlock stuff
}];

[operationATail addDependency:operationA];
[operationB addDependency:operationATail];
[self.queue addOperations:@[operationA, operationATail, operationB] waitUntilFinished:NO];

答案 1 :(得分:0)

为什么你不能在完成块a内部调用操作,这就是完成块的用途。

[operationA setCompletionBlock:^{
    NSLog(@"-- Completion Block A");
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    NSOperation *operationB = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"- Running operation B");
        [NSThread sleepForTimeInterval:2];
        NSLog(@"- Done operation B");
    }];
    [queue addOperations:@[operationB] waitUntilFinished:NO];
}];


[operationA setCompletionBlock:^{
    NSLog(@"-- Completion Block A when we dont need B");
}];

使用

可以通过一些更好的方式来执行此操作
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
     [self operationB];
}

答案 2 :(得分:0)

  1. 避免完成块-它们是不合适的过时机制 用于同步任何操作或其通信。

  2. 引入依赖关系(B取决于A)意味着B仅在A成功完成后运行。

  3. 出于这个原因,任何简单的数据对象都可用于在这两个操作之间安全地“传递信息”-可以简单地共享它(只要您在定义这两个操作的块之外创建它即可。 B”运行,它可以假定“ A”已经在数据对象中放入了所需的信息,并可以简单地访问它。

    self.queue = [[NSOperationQueue alloc] init];
    
    NSMutableDictionary *info = [NSMutableDictionary new]; // data object for communication
    
    NSOperation *operationA = [NSBlockOperation blockOperationWithBlock:^{
      NSLog(@"- Running operation A");
     [NSThread sleepForTimeInterval:1.2];
    
      info[@"DoThis"] = @YES;
      info[@"DoThat"] = @NO;
      NSLog(@"- Done operation A");
    }];
    
    NSOperation *operationB = [NSBlockOperation blockOperationWithBlock:^{
       NSLog(@"- Running operation B");
    
      if ([info[@"DoThis"] boolValue] NSLog(@"Op A said to do this.");
      if ([info[@"DoThat"] boolValue] NSLog(@"Op A said to do that.");
    
      [NSThread sleepForTimeInterval:2];
      NSLog(@"- Done operation B");
    }];
    
    [operationB addDependency:operationA];
    [self.queue addOperations:@[operationA, operationB] waitUntilFinished:NO];