IOS 6 में ब्लॉक पूरा करने के लिए dispatch_get_current_queue () के विकल्प?


101

मेरे पास एक विधि है जो एक ब्लॉक और एक पूर्ण ब्लॉक को स्वीकार करती है। पहला ब्लॉक पृष्ठभूमि में चलना चाहिए, जबकि पूरा ब्लॉक जो कुछ भी कतार में चलना चाहिए था विधि को बुलाया गया था।

बाद के लिए मैंने हमेशा इस्तेमाल किया dispatch_get_current_queue(), लेकिन ऐसा लगता है कि यह आईओएस 6 या उच्चतर में पदावनत है। इसके बजाय मुझे क्या उपयोग करना चाहिए?


आप कहते हैं कि dispatch_get_current_queue()आईओएस 6 में पदावनत क्यों किया गया है? डॉक्स इसके बारे में कुछ नहीं कहते हैं
jere

3
संकलक इसके बारे में शिकायत करता है। कोशिश करो।
cfischer

4
@jere हेडर फ़ाइल की जाँच करें, यह बताता है कि यह
विकृत है

सबसे अच्छा अभ्यास क्या है, इस पर चर्चा के अलावा, मैं [NSOperationQueue currentQueue] देखता हूं जो इस प्रश्न का उत्तर दे सकता है। इसके उपयोग के रूप में caveats के बारे में निश्चित नहीं है।
मैट

पाया गया चेतावनी ------ [NSOperationQueue currentQueue] डिस्पैच_गेट_कंट्री_क्व्यू () ----- के समान नहीं है, यह कभी-कभी शून्य ---- डिस्पैच_संक्यू (डिस्पैच_ग्लॉट_क्लिप्यू (0, 0), ^ {NSLog (@ "q) (0) देता है। 0)% @ ", dispatch_get_current_queue ()); NSLog (@" cq (0,0)% @ "है, [NSOperationQueue currentQueue]);}); ----- q (0,0) है <OS_dispatch_queue_root: com.apple.root.default-qos [0x100195140]> cq (0,0) है (शून्य) ---- - विचलित या प्रेषण_get_current_queue () नहीं लगता सभी स्थितियों में वर्तमान कतार की रिपोर्टिंग के लिए मैं एकमात्र समाधान देखता हूं
Godzilla

जवाबों:


64

"जिस भी पंक्ति पर कॉलर चल रहा था उस पर चलने" का पैटर्न आकर्षक है, लेकिन अंततः एक महान विचार नहीं है। वह कतार कम प्राथमिकता वाली कतार, मुख्य कतार या विषम गुणों वाली कोई अन्य कतार हो सकती है।

मेरा पसंदीदा दृष्टिकोण यह कहना है कि "पूरा होने वाला ब्लॉक इन गुणों के साथ कार्यान्वयन परिभाषित कतार पर चलता है: x, y, z", और कॉलर से अधिक नियंत्रण चाहते हैं, तो ब्लॉक को एक विशेष कतार में भेजने दें। निर्दिष्ट करने के लिए गुणों का एक विशिष्ट सेट "सीरियल, गैर-रीएंन्ट्रेंट, और किसी अन्य अनुप्रयोग-दृश्य कतार के संबंध में एसिंक्स" जैसा होगा।

** EDIT **

कैटफ़िश_मैन नीचे दी गई टिप्पणियों में एक उदाहरण देता है, मैं इसे केवल अपने उत्तर में जोड़ रहा हूं।

- (void) aMethodWithCompletionBlock:(dispatch_block_t)completionHandler     
{ 
    dispatch_async(self.workQueue, ^{ 
        [self doSomeWork]; 
        dispatch_async(self.callbackQueue, completionHandler); 
    } 
}

7
पूरी तरह से सहमत। आप देख सकते हैं कि Apple हमेशा इसका अनुसरण करता है; जब भी आप मुख्य कतार पर कुछ करना चाहते हैं तो आपको हमेशा मुख्य कतार में भेजने की आवश्यकता होती है क्योंकि सेब हमेशा गारंटी देता है कि आप एक अलग धागे पर हैं। अधिकांश समय आप डेटा प्राप्त करने / हेरफेर करने के लिए एक लंबी चलने वाली प्रक्रिया की प्रतीक्षा कर रहे हैं और फिर आप इसे अपने पूर्ण ब्लॉक में बैकग्राउंड में प्रोसेस कर सकते हैं और फिर यूआई कॉल को मुख्य कतार में एक डिस्पैच ब्लॉक में चिपका सकते हैं। इसके अलावा, यह हमेशा अच्छा लगता है कि डेवलपर्स के पैटर्न के लिए उपयोग किए जाने के बाद से एप्पल उम्मीदों के मामले में क्या सेट करता है।
जैक लॉरेंस 2

1
महान जवाब .. लेकिन मैं कम से कम कुछ नमूना कोड के लिए उम्मीद कर रही थी वर्णन करने के लिए आप क्या कह रहे हैं
abbood

3
- (void) aMethodWithCompletionBlock: (dispatch_block_t) पूर्णहैंडलर {dispatch_async (self.workQueue, ^ {[स्वयं doSomeWork]; dispatch -async (self.callbackQueue, पूर्णहैंडलर)};}
कैटफ़िश_मन

(पूरी तरह से तुच्छ उदाहरण के लिए)
कैटफ़िश_मैन

3
यह सामान्य मामले में संभव नहीं है, क्योंकि यह संभव है (और वास्तव में काफी संभावना है) एक साथ एक से अधिक कतार में होने के कारण, dispatch_sync () और dispatch_set_target_queue () के कारण। सामान्य मामले के कुछ सबसेट हैं जो संभव हैं।
कैटफ़िश_मन

27

यह मौलिक रूप से एपीआई के लिए गलत दृष्टिकोण है जिसे आप लेने के लिए वर्णन कर रहे हैं। अगर कोई API ब्लॉक और रनिंग को पूरा करने के लिए ब्लॉक स्वीकार करता है, तो निम्नलिखित तथ्यों को सच होना चाहिए:

  1. "ब्लॉक टू रन" को एक आंतरिक कतार पर चलाया जाना चाहिए, उदाहरण के लिए एक कतार जो एपीआई के लिए निजी है और इसलिए पूरी तरह से उस एपीआई के नियंत्रण में है। इसका एकमात्र अपवाद यदि एपीआई विशेष रूप से यह घोषणा करता है कि ब्लॉक को मुख्य कतार या वैश्विक समवर्ती कतारों में से एक पर चलाया जाएगा।

  2. पूर्ण ब्लॉक को हमेशा एक टपल (कतार, ब्लॉक) के रूप में व्यक्त किया जाना चाहिए , जब तक कि # 1 सच के लिए समान धारणाएं न हों, जैसे पूरा होने वाला ब्लॉक एक ज्ञात वैश्विक कतार में चलाया जाएगा। पूर्ण किए गए ब्लॉक को पास-की गई कतार पर async भेजा जाना चाहिए।

ये केवल शैलीगत बिंदु नहीं हैं, वे पूरी तरह से आवश्यक हैं यदि आपका एपीआई गतिरोधों या अन्य किनारे-मामले के व्यवहार से सुरक्षित है जो किसी दिन आपको निकटतम पेड़ से लटकाएंगे। :-)


11
उचित लगता है, लेकिन किसी कारण के लिए कि ऐप्पल द्वारा अपने स्वयं के एपीआई के लिए दृष्टिकोण नहीं लिया गया है: एक पूर्ण ब्लॉक लेने वाले अधिकांश तरीके भी एक कतार नहीं लेते हैं ...
cfischer

2
सच है, और मेरे पिछले दावे को कुछ हद तक संशोधित करने के लिए, अगर यह स्पष्ट रूप से स्पष्ट है कि पूरा होने वाला ब्लॉक मुख्य कतार या वैश्विक समवर्ती कतार पर चलाया जाएगा। मैं अपना उत्तर बदलने के लिए संकेत करूँगा।
jkh

Apple के उस दृष्टिकोण पर टिप्पणी नहीं करने के लिए: Apple हमेशा प्रति परिभाषा "सही" नहीं है। उचित तर्क हमेशा किसी विशेष प्राधिकारी की तुलना में अधिक मजबूत होते हैं, जिसे कोई भी वैज्ञानिक पुष्टि करेगा। मुझे लगता है कि उपरोक्त उत्तर एक उचित सॉफ्टवेयर आर्किटेक्चर के दृष्टिकोण से बहुत अच्छी तरह से बताता है।
वर्नर अल्टिविशर

14

अन्य उत्तर महान हैं, लेकिन मेरे लिए उत्तर संरचनात्मक है। मैं इस तरह एक विधि है कि एक सिंगलटन पर है:

- (void) dispatchOnHighPriorityNonMainQueue:(simplest_block)block forceAsync:(BOOL)forceAsync {
    if (forceAsync || [NSThread isMainThread])
        dispatch_async_on_high_priority_queue(block);
    else
        block();
}

जिसके दो आश्रित हैं, जो हैं:

static void dispatch_async_on_high_priority_queue(dispatch_block_t block) {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), block);
}

तथा

typedef void (^simplest_block)(void); // also could use dispatch_block_t

इस तरह मैं अपने कॉल को दूसरे थ्रेड पर भेजने के लिए केंद्रीकृत करता हूं।


12

आपको dispatch_get_current_queueपहली बार में अपने उपयोग के बारे में सावधान रहना चाहिए । हेडर फ़ाइल से:

केवल डिबगिंग और लॉगिंग उद्देश्यों के लिए अनुशंसित:

जब तक यह वैश्विक कतारों में से एक न हो, या कोड स्वयं बनाई गई कतार में से एक हो, तब तक कोड के बारे में कोई धारणा नहीं बनानी चाहिए। कोड को यह नहीं मान लेना चाहिए कि कतार पर तुल्यकालिक निष्पादन गतिरोध से सुरक्षित है यदि वह कतार डिस्पैच_गेट_क्रांति_क्व्यू () द्वारा वापस नहीं किया गया है।

आप या तो दो काम कर सकते हैं:

  1. आपके द्वारा मूल रूप से पोस्ट की गई कतार (यदि आपने इसे बनाया है dispatch_queue_create) का संदर्भ रखें , और तब से इसका उपयोग करें।

  2. के माध्यम से प्रणाली परिभाषित कतारों का उपयोग करें dispatch_get_global_queue, और एक ट्रैक रखें जिसका आप उपयोग कर रहे हैं।

प्रभावी रूप से पहले से आप जिस कतार में हैं, उस पर नज़र रखने के लिए सिस्टम पर निर्भर रहते हुए, आप इसे स्वयं करने जा रहे हैं।


16
अगर हम यह dispatch_get_current_queue()पता लगाने के लिए कि हम कौन सी कतार का उपयोग नहीं कर सकते हैं, तो हम "आपके द्वारा मूल रूप से पोस्ट की गई कतार का संदर्भ" कैसे रख सकते हैं ? कभी-कभी कोड को यह जानने की आवश्यकता होती है कि यह किस कतार पर चल रहा है, इसका कोई नियंत्रण या ज्ञान नहीं है। मेरे पास बहुत सारे कोड हैं जो पृष्ठभूमि की कतार पर (और चाहिए) निष्पादित किए जा सकते हैं, लेकिन कभी-कभी उन्हें gui (प्रगति बार, आदि) को अपडेट करने की आवश्यकता होती है, और इसलिए उन कार्यों के लिए मुख्य कतार में भेजने के लिए_sync () को भेजने की आवश्यकता होती है। यदि पहले से ही मुख्य कतार पर है, तो डिस्पैच_sync () हमेशा के लिए लॉक हो जाएगा। इसके लिए मुझे अपने कोड को फिर से भेजने में महीनों लगेंगे।
अभि बेकर्ट

3
मुझे लगता है कि NSURLConnection उसी थ्रेड पर अपने पूर्ण कॉलबैक देता है, जिससे इसे बुलाया जाता है। क्या यह उसी API "डिस्पैच_गेट_क्रांति_एक्यू" का उपयोग करेगा जिसे कॉलबैक के समय उपयोग की जाने वाली कतार को स्टोर करने के लिए कहा जाता है?
डिफैक्टोडिटी

5

Apple ने अपदस्थ dispatch_get_current_queue()कर दिया था , लेकिन दूसरी जगह एक छेद छोड़ दिया था , इसलिए हम अभी भी वर्तमान प्रेषण कतार प्राप्त करने में सक्षम हैं:

if let currentDispatch = OperationQueue.current?.underlyingQueue {
    print(currentDispatch)
    // Do stuff
}

यह मुख्य कतार के लिए कम से कम काम करता है। ध्यान दें, वह underlyingQueueसंपत्ति iOS 8 के बाद से उपलब्ध है।

यदि आपको मूल कतार में पूर्ण ब्लॉक करने की आवश्यकता है, तो आप OperationQueueसीधे उपयोग भी कर सकते हैं , मेरा मतलब जीसीडी के बिना है।



0

यह मेरा भी जवाब है। इसलिए मैं हमारे उपयोग के मामले के बारे में बात करूंगा।

हमारे पास सेवाओं की परत और UI परत (अन्य परतों के बीच) है। सेवाओं की परत पृष्ठभूमि में कार्यों को चलाती है। (डेटा हेरफेर कार्य, CoreData कार्य, नेटवर्क कॉल आदि)। UI लेयर की जरूरतों को पूरा करने के लिए सर्विस लेयर में एक दो ऑपरेशन कतारें होती हैं।

यूआई परत अपने काम को करने के लिए सेवाओं की परत पर निर्भर करती है और फिर एक सफलता पूर्ण ब्लॉक चलाती है। इस ब्लॉक में UIKit कोड हो सकता है। एक सरल उपयोग मामला सर्वर से सभी संदेश प्राप्त करने और संग्रह दृश्य को फिर से लोड करने के लिए है।

यहां हम गारंटी देते हैं कि जिन ब्लॉकों को सेवाओं की परत में पारित किया गया है उन्हें उस कतार पर भेज दिया जाता है जिस पर सेवा को लागू किया गया था। चूंकि dispatch_get_current_queue एक पदावनत विधि है, इसलिए हम कॉलर की वर्तमान कतार प्राप्त करने के लिए NSOperationQueue.currentQueue का उपयोग करते हैं। इस संपत्ति पर महत्वपूर्ण ध्यान दें।

किसी रनिंग ऑपरेशन के संदर्भ में बाहर से इस विधि को कॉल करने पर आमतौर पर शून्य वापस आ जाता है।

चूँकि हम हमेशा एक ज्ञात कतार (हमारी कस्टम कतार और मुख्य कतार) पर अपनी सेवाओं का आह्वान करते हैं, यह हमारे लिए अच्छा काम करता है। हमारे पास ऐसे मामले हैं जहां सर्विसए सर्विसबी को कॉल कर सकती है जिसे सर्विससी कह सकते हैं। चूंकि हम नियंत्रित करते हैं कि पहली सेवा कॉल कहां से की जा रही है, हम जानते हैं कि बाकी सेवाएँ समान नियमों का पालन करेंगी।

तो NSOperationQueue.currentQueue हमेशा हमारी एक पंक्ति या MainQueue लौटाएगा।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.