objective-c - NSBlockOperation 是否可以在执行时自行取消,从而取消依赖的 NSOperation?
问题描述
我有许多NSBlockOperation
具有依赖关系的链。如果链中早期的一项操作失败 - 我希望其他操作不运行。根据文档,这应该很容易从外部完成 - 如果我取消一个操作,所有相关的操作都应该自动取消。
但是 - 如果只有我的操作的执行块“知道”它在执行时失败了 - 它可以cancel
自己工作吗?
我尝试了以下方法:
NSBlockOperation *op = [[NSBlockOperation alloc] init];
__weak NSBlockOperation *weakOpRef = op;
[takeScreenShot addExecutionBlock:^{
LOGInfo(@"Say Cheese...");
if (some_condition == NO) { // for some reason we can't take a photo
[weakOpRef cancel];
LOGError(@"Photo failed");
}
else {
// take photo, process it, etc.
LOGInfo(@"Photo taken");
}
}];
op
但是,当我运行它时,即使op
已取消,也会执行依赖于的其他操作。由于它们是依赖的 - 它们肯定不会在op
完成之前开始,并且我(在调试器和使用日志中)验证了块返回之前的isCancelled
状态。队列仍然像成功完成一样执行它们。op
YES
op
然后我进一步挑战了文档,如下所示:
NSOperationQueue *myQueue = [[NSOperationQueue alloc] init];
NSBlockOperation *op = [[NSBlockOperation alloc] init];
__weak NSBlockOperation *weakOpRef = takeScreenShot;
[takeScreenShot addExecutionBlock:^{
NSLog(@"Say Cheese...");
if (weakOpRef.isCancelled) { // Fail every once in a while...
NSLog(@"Photo failed");
}
else {
[NSThread sleepForTimeInterval:0.3f];
NSLog(@"Photo taken");
}
}];
NSOperation *processPhoto = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"Processing Photo...");
[NSThread sleepForTimeInterval:0.1f]; // Process
NSLog(@"Processing Finished.");
}];
// setup dependencies for the operations.
[processPhoto addDependency: op];
[op cancel]; // cancelled even before dispatching!!!
[myQueue addOperation: op];
[myQueue addOperation: processPhoto];
NSLog(@">>> Operations Dispatched, Wait for processing");
[eventQueue waitUntilAllOperationsAreFinished];
NSLog(@">>> Work Finished");
但是很震惊地在日志中看到以下输出:
2020-11-05 16:18:03.803341 >>> Operations Dispatched, Wait for processing
2020-11-05 16:18:03.803427 Processing Photo...
2020-11-05 16:18:03.813557 Processing Finished.
2020-11-05 16:18:03.813638+0200 TesterApp[6887:111445] >>> Work Finished
请注意:取消的操作从未运行过 - 但依赖项processPhoto
已执行,尽管它依赖于op
.
任何人的想法?
解决方案
好的。我想我解开了这个谜。我只是误解了[NSOperation cancel]
文档。
它说:
在 macOS 10.6 及更高版本中,如果操作在队列中但正在等待未完成的相关操作,则这些操作随后将被忽略。因为它已经被取消了,所以这种行为允许操作队列更快地调用操作的 start 方法并将对象从队列中清除。如果取消不在队列中的操作,此方法会立即将该对象标记为已完成。在每种情况下,将对象标记为就绪或完成会导致生成适当的 KVO 通知。
我认为如果操作 B 依赖于操作 A - 这意味着当 A 被取消时(因此 - 没有完成它的工作),那么 B 也应该被取消 - 因为从语义上讲,直到 A 完成它的工作才能开始。
那是一厢情愿...
文档说的是不同的。当您取消 B 时,尽管依赖于 A,但它不会等待 A 完成,然后再将其从队列中删除。如果 A 还没有完成 - 取消 B 将立即从队列中删除它 - 因为它仍然处于未决状态(A 的完成)。
Soooo.....为了完成我的计划,我需要引入我自己的“依赖”机制,可能是以一组布尔属性的形式,比如isPhotoTaken
等isPhotoProcessed
,然后依赖于这些的操作,需要检查它的(执行块的)前导码是否所有必需的先前操作实际上已成功完成。
继承 NSBlockOperation 可能是值得的,如果任何“依赖项”已被取消,则覆盖调用“开始”以跳至完成的逻辑......但这是一个长期的目标,可能难以实现。
最后,我写了这个快速的子类,它似乎工作了——当然需要更深入的检查:
@interface MYBlockOperation : NSBlockOperation {
}
@end
@implementation MYBlockOperation
- (void)start {
if ([[self valueForKeyPath:@"dependencies.@sum.cancelled"] intValue] > 0)
[self cancel];
[super start];
}
@end
当我在原始问题中将 NSBlockOperation 替换为 MYBlockOperation 时(以及我的其他测试,行为与我预期的一样。