我们遇到一个关于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 ?
答案 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)
避免完成块-它们是不合适的过时机制 用于同步任何操作或其通信。
引入依赖关系(B取决于A)意味着B仅在A成功完成后运行。
出于这个原因,任何简单的数据对象都可用于在这两个操作之间安全地“传递信息”-可以简单地共享它(只要您在定义这两个操作的块之外创建它即可。 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];