我有一个长时间运行的循环我想在后台运行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下创建一个保留周期。在ARC,我相信__block
存储保留。如果是这样,我们就遇到了麻烦,因为NSBlockOperation
也对传入的块保持强有力的引用,现在它对操作有很强的参考作用,它对传入的块有很强的参考作用,其中......
它不太优雅,但使用明确的弱引用应该打破循环:
NSBlockOperation *operation = [[NSBlockOperation alloc] init];
__weak NSBlockOperation *weakOperation = operation;
[operation addExecutionBlock:^{
while( ! [weakOperation isCancelled]){
//do something...
}
}];
任何有更优雅解决方案想法的人,请评论!
加强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];
使用Swift 5,您可以使用BlockOperation
创建可取消的addExecutionBlock(_:)
。 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()
}
这打印:
0
1
2
3
...
Cancelled
No retain cycle
我希望有一个可取消的块,一旦单元格从屏幕滚动,我的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
}
}