2011-11-13 9 views
45

को रद्द करने के लिए कैसे करें मेरे पास एक लंबे समय तक चलने वाला लूप है जिसे मैं NSOperation के साथ पृष्ठभूमि में चलाने के लिए चाहता हूं। मैं एक ब्लॉक का उपयोग करना चाहता हूं:NSBlockOperation

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

सवाल यह है कि, मैं यह देखने के लिए कैसे जांचूं कि यह रद्द कर दिया गया है या नहीं। ब्लॉक कोई तर्क नहीं लेता है, और operation ब्लॉक द्वारा पकड़ा गया समय शून्य है। ब्लॉक ऑपरेशन रद्द करने का कोई तरीका नहीं है?

उत्तर

65

दोह। प्रिय भावी googlers: निश्चित रूप से ब्लॉक द्वारा प्रतिलिपि operation शून्य है, लेकिन यह कॉपी करने के लिए नहीं है। यह __block इसलिए की तरह साथ योग्य हो सकते हैं:

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

अद्यतन:

आगे ध्यान करने पर, यह मेरे लिए होता है कि यह एक एआरसी के तहत चक्र को बनाए रखने का निर्माण करेगा। एआरसी में, मेरा मानना ​​है कि __block संग्रहण बनाए रखा गया है। यदि हां, तो हम परेशानी में हैं, क्योंकि NSBlockOperation भी पारित ब्लॉक में एक मजबूत संदर्भ रखता है, जो अब ऑपरेशन के लिए एक मजबूत संदर्भ है, जिसमें पारित ब्लॉक के लिए एक मजबूत संदर्भ है, जो ...

यह है थोड़ा कम सुंदर, लेकिन कोई स्पष्ट कमजोर संदर्भ का उपयोग करते हुए चक्र को तोड़ने चाहिए:

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

किसी को भी एक और अधिक सुरुचिपूर्ण समाधान के उपाय हैं कि, कृपया टिप्पणी!

+1

बहुत उपयोगी! आपके पास एक टाइपो है, यद्यपि: रद्द किया जाना चाहिए रद्द किया गया – hsdev

+0

फिक्स्ड! धन्यवाद। मेरे पास कोडरनर अब भविष्य में इन शर्मिंदगी से बचाने के लिए है ;-) – jemmons

+2

क्या इस कार्यान्वयन में कोई बग नहीं है? जब कमजोर हो जाता है तो ऑपरेशन शून्य हो जाता है, क्या यह लूपिंग जारी रखने की कोशिश नहीं करेगा? i.e.! nil == सच। लूप की स्थिति नहीं होनी चाहिए (कमजोर ऑपरेशन &&! [कमजोर ऑपरेशन रद्द है])? –

43

जेमन्स जवाब को मजबूत करने के लिए। 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]; 
+0

बस यह सुनिश्चित करना चाहते हैं कि आप ब्लॉक नहीं कर सकते हैं आप इस पर क्या कर सकते हैं? –

+1

'blockOperationWithBlock' आमतौर पर बहुत सुविधाजनक है लेकिन दुर्भाग्य से आप इस विधि का उपयोग करते समय ऑपरेशन का संदर्भ नहीं प्राप्त कर सकते हैं (वास्तव में आप इसे घोषित करने के बाद एक प्राप्त कर सकते हैं, लेकिन आप वास्तविक संदर्भ में इस संदर्भ का उपयोग नहीं कर सकते हैं)। ऑपरेशन रद्द होने पर जांचने के लिए आपको एक संदर्भ की आवश्यकता है। – Robert

+0

मैं इसे खींचने में कामयाब रहा लेकिन फिर ब्लॉक ऑपरेशन को __weak __block के रूप में घोषित करने की आवश्यकता है ताकि ब्लॉक को वास्तविक सूचक की प्रतिलिपि बनाने के बजाय इसका संदर्भ दिया जा सके। –

2
स्विफ्ट 4 के साथ

, आप एक रद्द करने योग्य BlockOperationaddExecutionBlock(_:) साथ बना सकते हैं। addExecutionBlock(_:) निम्नलिखित declaration है:

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 के साथ सूची।

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 
0


निम्नलिखित खेल का मैदान कोड को देखने का तरीका यह है कि वहाँ कोई एक BlockOperation उपवर्ग उदाहरण और इसके निष्पादन ब्लॉक के बीच चक्र को बनाए रखने है पता चलता मैं रद्द करने योग्य ब्लॉक चाहता था कि मेरे UICollectionViewController कोशिकाओं को स्क्रॉल किए जाने के बाद आसानी से रद्द कर दिया जा सके स्क्रीन से बाहर। ब्लॉक नेटवर्क ओप नहीं कर रहे हैं, वे छवि संचालन कर रहे हैं (आकार बदलना, फसल आदि)। ब्लॉक को स्वयं को जांचने का संदर्भ होना चाहिए कि क्या उनके सेशन को रद्द कर दिया गया है, और अन्य उत्तरों में से कोई भी नहीं (जब मैंने इसे लिखा था) प्रदान किया।

यहाँ क्या मुझे (स्विफ्ट 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 
     } 
    }