如何取消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一个可取消的BlockOperation
。 addExecutionBlock(_:)
具有以下声明 :
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
实例和其执行块之间的保留周期,您必须在执行块内部使用具有weak
或unowned
引用的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 } }