如何取消NSBlockOperation

jem*_*ons 49 nsoperation nsoperationqueue objective-c-blocks

我有一个长时间运行的循环,我想在后台运行一个NSOperation.我想用一个块:

NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
   while(/* not canceled*/){
      //do something...
   }
}];
Run Code Online (Sandbox Code Playgroud)

问题是,如何检查它是否被取消.该块不带任何参数,并且operation在块被捕获时为零.有没有办法取消块操作?

jem*_*ons 69

卫生署.亲爱的Google未来:当然,operation当由块复制为零,但它不具有被复制.它可以__block像这样限定:

//THIS MIGHT LEAK! See the update below.
__block NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
   while( ! [operation isCancelled]){
      //do something...
   }
}];
Run Code Online (Sandbox Code Playgroud)

更新:

进一步冥想后,我发现这将在ARC下创建一个保留周期.在ARC中,我相信__block存储是保留的.如果是这样,我们就遇到了麻烦,因为它NSBlockOperation也保留了对传入块的强引用,现在它对操作有很强的参考作用,它对传入的块有强烈的引用,其中......

它不太优雅,但使用明确的弱引用应该打破循环:

NSBlockOperation *operation = [[NSBlockOperation alloc] init];
__weak NSBlockOperation *weakOperation = operation;
[operation addExecutionBlock:^{
   while( ! [weakOperation isCancelled]){
      //do something...
   }
}];
Run Code Online (Sandbox Code Playgroud)

任何有更优雅解决方案想法的人,请评论!

  • 这个实现中没有错误吗?当weakOperation变为nil时,它会不会尝试继续循环?ie!nil == true.循环条件不应该是while(weakOperation &&![weakOperation isCancelled])? (2认同)
  • @MarcPalmer鉴于这个块是在'weakOperation`操作中运行的,我不认为`weakOperation`可能在它内部为零.当然,我无法在测试代码中制造这种情况.你有一个例子吗? (2认同)
  • 无论如何都不需要@ asma22`__block`.它用于表示导入块的变量应该是*mutable*.我们不需要`weakOperation`是可变的,因为:A)它是一个指针.我们可以在不改变它的情况下弄乱它所指出的内容.如果我们想让它指向其他东西,它只需要是可变的.而且B)即使它不是,我们只是从中读取,所以它仍然不需要是可变的.参见[Apple的文档](https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/Blocks/Articles/bxVariables.html#//apple_ref/doc/uid/TP40007502-CH6-SW6) (2认同)

Rob*_*ert 45

加强jemmons的回答. WWDC 2012会议211 - 构建Concurent用户界面(33分钟)

NSOperationQueue* myQueue = [[NSOperationQueue alloc] init];
NSBlockOperation* myOp = [[NSBlockOperation alloc] init];

// Make a weak reference to avoid a retain cycle
__weak NSBlockOperation* myWeakOp = myOp;

[myOp addExecutionBlock:^{
    for (int i = 0; i < 10000; i++) {
        if ([myWeakOp isCancelled]) break;
        precessData(i);
    }
}];
[myQueue addOperation:myOp];
Run Code Online (Sandbox Code Playgroud)


Ima*_*tit 8

使用Swift 4,您可以创建一个可取消BlockOperationaddExecutionBlock(_:).addExecutionBlock(_:)有以下声明:

func addExecutionBlock(_ block: @escaping () -> Void)
Run Code Online (Sandbox Code Playgroud)

将指定的块添加到接收器要执行的块列表中.


以下示例显示了如何实现addExecutionBlock(_:):

let blockOperation = BlockOperation()

blockOperation.addExecutionBlock({ [unowned blockOperation] in
    for i in 0 ..< 10000 {
        if blockOperation.isCancelled {
            print("Cancelled")
            return // or break
        }
        print(i)
    }
})
Run Code Online (Sandbox Code Playgroud)

请注意,为了防止BlockOperation实例与其执行块之间的保留周期,您必须在执行块内使用带有weakunowned引用的捕获列表blockOperation.


以下Playground代码显示如何检查BlockOperation子类实例与其执行块之间没有保留周期:

import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

class TestBlockOperation: BlockOperation {
    deinit {
        print("No retain cycle")
    }
}

do {
    let queue = OperationQueue()

    let blockOperation = TestBlockOperation()
    blockOperation.addExecutionBlock({ [unowned blockOperation] in
        for i in 0 ..< 10000 {
            if blockOperation.isCancelled {
                print("Cancelled")
                return // or break
            }
            print(i)
        }
    })

    queue.addOperation(blockOperation)

    Thread.sleep(forTimeInterval: 0.5)
    blockOperation.cancel()
}
Run Code Online (Sandbox Code Playgroud)

这打印:

0
1
2
3
...
Cancelled
No retain cycle
Run Code Online (Sandbox Code Playgroud)