如何取消NSBlockOperation

我有一个很长的运行循环,我想用NSOperation在后台运行。 我想用一个块:

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

问题是,如何检查是否取消。 该块没有任何参数,当它被块捕获时, operation是零。 有没有办法取消块操作?

卫生署。 亲爱的未来googlers:当然operation是零,当被块复制,但它不必被复制。 它可以像__block那样限定:

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

更新:

经过进一步的冥想,我发现这将在ARC下创build一个保留周期。 在ARC中,我相信__block存储被保留。 如果是这样,我们有麻烦了,因为NSBlockOperation还保留了一个强大的引用到块中传递,现在有一个强有力的参考操作,它有一个强大的参考块中,…

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

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

任何人有一个更优雅的解决scheme的想法,请评论!

加强jemmons的回答。 WWDC 2012会议211 – build立一致的用户界面 (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]; 

使用Swift 4,你可以用addExecutionBlock(_:)创build一个可取消的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的捕获列表。


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

这打印:

 1 2 3 ... Cancelled No retain cycle 

我想要有可取消的块,我的UICollectionViewController可以很容易地取消一旦单元格滚动屏幕。 块不做networking操作,他们正在做图像操作(resize,裁剪等)。 块本身需要有一个参考来检查他们的操作是否已被取消,而其他答案(在我写这个时候)没有提供。

下面是对我BlockOperation的东西(Swift 3) – 使得对BlockOperation采取弱引用的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 } }