2011-11-13 40 views
45

Tôi có một vòng chạy dài tôi muốn chạy ở chế độ nền với NSOperation. Tôi muốn sử dụng một khối:Làm thế nào để hủy bỏ NSBlockOperation

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

Câu hỏi là, làm thế nào để kiểm tra xem nó có bị hủy hay không. Khối không nhận bất kỳ đối số nào và operation là 0 tại thời điểm khối bị chặn. Không có cách nào để hủy các hoạt động chặn?

Trả lời

65

Doh. Kính gửi các googlers trong tương lai: tất nhiên operation là không khi được sao chép bởi khối, nhưng không để được sao chép. Nó có thể được đủ điều kiện với __block như vậy:

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

UPDATE:

Khi thiền hơn nữa, nó xảy ra với tôi rằng điều này sẽ tạo ra một chu kỳ giữ lại dưới ARC. Trong ARC, tôi tin rằng bộ nhớ __block được giữ lại. Nếu vậy, chúng tôi đang gặp rắc rối, bởi vì NSBlockOperation cũng giữ một tham chiếu mạnh mẽ đến khối được thông qua, mà bây giờ có một tham chiếu mạnh mẽ đến các hoạt động, trong đó có một tham chiếu mạnh mẽ đến… Continue một chút ít thanh lịch, nhưng sử dụng một tham chiếu yếu rõ ràng nên phá vỡ chu kỳ:

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

Bất cứ ai có ý tưởng cho một giải pháp thanh lịch hơn, xin vui lòng bình luận!

+1

Rất hữu ích! Bạn có lỗi chính tả: isCanceled phải là isCancelled – hsdev

+0

Đã sửa lỗi! Cảm ơn. Tôi có CodeRunner bây giờ để cứu tôi khỏi những điều xấu hổ này trong tương lai ;-) – jemmons

+2

Không có lỗi trong quá trình thực hiện này? Khi weakOperation trở thành nil sẽ không cố gắng tiếp tục lặp? tức là! nil == đúng. Điều kiện vòng lặp không nên trong khi (weakOperation &&! [WeakOperation isCancelled])? –

43

Để tăng cường câu trả lời jemmons. WWDC 2012 session 211 - Building Concurent User Interfaces (33 phút trong)

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]; 
+0

Chỉ muốn đảm bảo bạn không thể thực hiện blockOperationWithBlock trên trình duyệt này? –

+1

'blockOperationWithBlock' thường rất thuận tiện nhưng tiếc là bạn không thể tham chiếu đến hoạt động khi bạn sử dụng phương thức này (bạn thực sự có thể nhận được sau khi bạn khai báo, nhưng bạn không thể sử dụng tham chiếu này trong khối thực). Bạn cần tham khảo để kiểm tra xem thao tác có bị hủy hay không. – Robert

+0

Tôi quản lý để kéo ra nhưng sau đó hoạt động khối cần phải được khai báo là __weak __block để khối lưu trữ một tham chiếu đến nó thay vì sao chép con trỏ thực tế. –

2

Với Swift 4, bạn có thể tạo một hủy BlockOperation với addExecutionBlock(_:). addExecutionBlock(_:) có sau declaration:

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

Thêm khối quy định vào danh sách người nhận của khối để thực hiện.


Ví dụ dưới đây cho thấy làm thế nào để thực hiện addExecutionBlock(_:):

let blockOperation = BlockOperation() 

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

Lưu ý rằng, để ngăn chặn một duy trì chu kỳ giữa BlockOperation dụ và khối thi của mình, bạn phải sử dụng ảnh chụp liệt kê một tài liệu weak hoặc unowned tham chiếu đến blockOperation bên trong khối thực thi.


Mã Sân chơi sau đây cho thấy làm thế nào để kiểm tra xem có được không duy trì chu kỳ giữa một trường hợp BlockOperation lớp con và khối thi của nó:

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

này in:

1 
2 
3 
... 
Cancelled 
No retain cycle 
0

Tôi muốn có các khối có thể hủy mà UICollectionViewController của tôi có thể dễ dàng hủy khi các ô được cuộn tắt màn hình. Các khối không làm ops mạng, họ đang làm các hoạt động hình ảnh (thay đổi kích thước, cắt xén, vv). Các khối chính họ cần phải có một tham chiếu để kiểm tra xem op của họ đã bị hủy bỏ, và không ai trong số các câu trả lời khác (tại thời điểm tôi đã viết này) với điều kiện đó.

Đây là những gì làm việc cho tôi (Swift 3) - làm cho khối mà phải mất một ref yếu đến BlockOperation, sau đó gói chúng trong BlockOperation khối chính nó:

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 
     } 
    } 

Sử dụng nó trong UICollectionViewController tôi:

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

Hoàn thành bức tranh:

class Weak<T: AnyObject> { 
     weak var value : T? 
     init (value: T) { 
      self.value = value 
     } 
    } 
Các vấn đề liên quan