如何取消NSBlockOperation

时间:2011-11-13 17:20:16

标签: objective-c-blocks nsoperation nsoperationqueue

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

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

问题是,如何查看是否已取消。该块不带任何参数,operation在块捕获时为零。有没有办法取消块操作?

4 个答案:

答案 0 :(得分:69)

卫生署。亲爱的未来googlers:当然,operation在被块复制时为零,但它没有被复制。可以使用__block来限定它:

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

更新:

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

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

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

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

答案 1 :(得分:45)

加强jemmons的回答。 WWDC 2012 session 211 - Building Concurent User Interfaces(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];

答案 2 :(得分:8)

使用Swift 5,您可以使用addExecutionBlock(_:)创建可取消的BlockOperationaddExecutionBlock(_:)有以下声明:

func addExecutionBlock(_ block: @escaping () -> Void)
  

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

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

let blockOperation = BlockOperation()

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

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

以下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()
}

打印:

0
1
2
3
...
Cancelled
No retain cycle

答案 3 :(得分:0)

我希望有一个可取消的块,一旦单元格从屏幕滚动,我的UICollectionViewController可以轻松取消。这些块没有进行网络操作,它们正在进行图像操作(调整大小,裁剪等)。这些块本身需要有一个引用来检查它们的op是否已被取消,并且没有其他答案(在我写这篇文章的时候)提供了。

这里有什么对我有用(Swift 3) - 制作弱引用BlockOperation的块,然后将它们包裹在BlockOperation块中:

    public extension OperationQueue {
        func addCancellableBlock(_ block: @escaping (BlockOperation?)->Void) -> BlockOperation {
            let op = BlockOperation.init()
            weak var opWeak = op
            op.addExecutionBlock {
                block(opWeak)
            }
            self.addOperation(op)
            return op
        }
    }

UICollectionViewController

中使用它
var ops = [IndexPath:Weak<BlockOperation>]()

    func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
        ...
        ops[indexPath] = Weak(value: DispatchQueues.concurrentQueue.addCancellableBlock({ (op) in
            cell.setup(obj: photoObj, cellsize: cellsize)
        }))
    }

    func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
        if let weakOp = ops[indexPath], let op: BlockOperation = weakOp.value {
            NSLog("GCV: CANCELLING OP FOR INDEXPATH \(indexPath)")
            op.cancel()
        }
    }

完成图片:

    class Weak<T: AnyObject> {
        weak var value : T?
        init (value: T) {
            self.value = value
        }
    }