जब के लिए enumerateObjectsUsingBlock बनाम का उपयोग करें


150

स्पष्ट अंतर के अलावा:

  • enumerateObjectsUsingBlockजब आपको इंडेक्स और ऑब्जेक्ट दोनों की आवश्यकता हो तब उपयोग करें
  • enumerateObjectsUsingBlockजब आपको स्थानीय चर संशोधित करने की आवश्यकता हो, तो इसका उपयोग न करें (मैं इस बारे में गलत था, बबूम का उत्तर देखें)

क्या enumerateObjectsUsingBlockआमतौर पर बेहतर या बदतर माना जाता है जब for (id obj in myArray)यह भी काम करेगा? क्या फायदे / नुकसान हैं (उदाहरण के लिए यह अधिक या कम प्रदर्शन करने वाला है)?


1
मुझे इसका उपयोग करना पसंद है यदि मुझे वर्तमान सूचकांक की आवश्यकता है।
Besi

जवाबों:


350

अंततः, जिस भी पैटर्न का आप उपयोग करना चाहते हैं और संदर्भ में अधिक स्वाभाविक रूप से उपयोग करें।

हालांकि for(... in ...)यह काफी सुविधाजनक और वाक्यविन्यास संक्षिप्त है, enumerateObjectsUsingBlock:इसमें कई विशेषताएं हैं जो दिलचस्प साबित हो सकती हैं या नहीं भी हो सकती हैं:

  • enumerateObjectsUsingBlock:तेज़ एन्युमरेशन की तुलना में तेज़ या तेज़ होगा (एन्यूमरेशन को लागू करने के for(... in ...)लिए NSFastEnumerationसमर्थन का उपयोग करता है )। फास्ट एन्यूमरेशन के लिए एक आंतरिक प्रतिनिधित्व से फास्ट एन्यूमरेशन के लिए अनुवाद की आवश्यकता होती है। वहाँ उपरि है। ब्लॉक-आधारित गणन संग्रह वर्ग को मूल भंडारण प्रारूप के सबसे तेज़ ट्रैवर्सल के रूप में सामग्री को तुरंत गणना करने की अनुमति देता है। सरणियों के लिए अप्रासंगिक, लेकिन यह शब्दकोशों के लिए एक बड़ा अंतर हो सकता है।

  • "स्थानीय वेरिएबल्स को संशोधित करने की आवश्यकता होने पर enumerateObjectsUsingBlock का उपयोग न करें" - सच नहीं है; आप अपने स्थानीय लोगों को घोषित कर सकते हैं __blockऔर वे ब्लॉक में लिखने योग्य होंगे।

  • enumerateObjectsWithOptions:usingBlock: समवर्ती या रिवर्स एन्यूमरेशन का समर्थन करता है।

  • शब्दकोशों के साथ, ब्लॉक आधारित गणना एक साथ कुंजी और मूल्य को पुनः प्राप्त करने का एकमात्र तरीका है।

व्यक्तिगत रूप से, मैं enumerateObjectsUsingBlock:अधिक बार उपयोग करता हूं for (... in ...), लेकिन - फिर से - व्यक्तिगत पसंद।


16
वाह, बहुत जानकारीपूर्ण। काश मैं इन दोनों जवाबों को स्वीकार कर सकता, लेकिन मैं चक के साथ जा रहा हूं क्योंकि यह मेरे साथ कुछ अधिक गूंजता है। साथ ही, मैंने __ब्लॉक की खोज करते हुए आपका ब्लॉग ( friday.com/bbum/2009/08/29/blocks-tips-tricks ) पाया और इससे भी अधिक सीखा। धन्यवाद।
पॉल व्हीलर

8
रिकॉर्ड के लिए, ब्लॉक-आधारित एन्यूमरेशन हमेशा "उतना तेज़ या तेज़ नहीं होता" mikeabdullah.net/slow-block-based-dEDIA-enumeration.html
माइक अब्दुल्ला

2
@VanDuTran ब्लॉक केवल एक अलग थ्रेड पर निष्पादित किए जाते हैं यदि आप उन्हें एक अलग थ्रेड पर निष्पादित करने के लिए कहते हैं। जब तक आप गणना के संगामिति विकल्प का उपयोग नहीं करते हैं, तब तक इसे उसी धागे पर निष्पादित किया जाएगा, जब कॉल किया गया था
bbum

2
निक लॉकवुड ने इस पर एक बहुत अच्छा लेख किया, और ऐसा लगता है enumerateObjectsUsingBlockकि अभी भी सरणियों और सेटों के लिए तेजी से गणना की तुलना में काफी धीमी है। मुझे आश्चर्य है क्योंकि? iosdevelopertips.com/objective-c/…
बॉब स्प्रीन

2
हालाँकि यह एक कार्यान्वयन विवरण की तरह है, इसे इस उत्तर में दो दृष्टिकोणों के बीच के अंतर के बीच में वर्णित किया जाना चाहिए जो enumerateObjectsUsingBlockब्लॉक के प्रत्येक इनवोकेशन को ऑटोरेलिज पूल (ओएस एक्स 10.10 के रूप में कम से कम) के साथ लपेटता है। यह प्रदर्शन अंतर की तुलना में for inऐसा नहीं करता है।
पोल

83

सरल गणना के लिए, बस तेजी से गणना (यानी एक for…in…लूप) का उपयोग करना अधिक मुहावरेदार विकल्प है। ब्लॉक विधि थोड़ी तेजी से हो सकती है, लेकिन यह ज्यादातर मामलों में ज्यादा मायने नहीं रखती है - कुछ प्रोग्राम सीपीयू-बाउंड होते हैं, और फिर भी यह दुर्लभ है कि लूप खुद के बजाय गणना के अंदर एक अड़चन होगी।

एक साधारण लूप भी अधिक स्पष्ट रूप से पढ़ता है। यहाँ दो संस्करणों की बॉयलरप्लेट दी गई है:

for (id x in y){
}

[y enumerateObjectsUsingBlock:^(id x, NSUInteger index, BOOL *stop){
}];

यहां तक ​​कि अगर आप सूचकांक को ट्रैक करने के लिए एक चर जोड़ते हैं, तो सरल लूप पढ़ना आसान है।

तो आपको कब इस्तेमाल करना चाहिए enumerateObjectsUsingBlock:? जब आप बाद में या कई स्थानों पर निष्पादित करने के लिए एक ब्लॉक का भंडारण कर रहे हैं। यह तब अच्छा होता है जब आप लूप बॉडी के लिए ओवरवॉल्ड रिप्लेसमेंट की बजाय एक फर्स्ट-क्लास फंक्शन के रूप में ब्लॉक का उपयोग कर रहे होते हैं।


5
enumerateObjectsUsingBlock:या तो सभी मामलों में तेज गति से समान गति या तेज होगी। for(... in ...)तीव्र गणना का उपयोग करता है जिसे आंतरिक डेटा संरचनाओं के कुछ अंतरिम प्रतिनिधित्व प्रदान करने के लिए संग्रह की आवश्यकता होती है। जैसा कि आप ध्यान दें, अप्रासंगिक होने की संभावना है।
bbum

4
+1When you're storing a block to execute later or in multiple places. It's good for when you're actually using a block as a first-class function rather than an overwrought replacement for a loop body.
स्टीव

7
@bbum मेरे स्वयं के परीक्षण बताते हैं कि enumerateObjects...वास्तव में धीमी गति से एक पाश के साथ तेजी से गणना हो सकती है। मैंने यह परीक्षा कई हजार बार चलाई; ब्लॉक और लूप की बॉडी कोड की एक ही लाइन थी [(NSOperation *)obj cancel];:। औसत: तेज एनम लूप - -[JHStatusBar dequeueStatusMessage:] [Line: 147] Fast enumeration time (for..in..loop): 0.000009और ब्लॉक के लिए - -[JHStatusBar dequeueStatusMessage:] [Line: 147] Enumeration time using block: 0.000043। अजीब है कि समय का अंतर इतना बड़ा और सुसंगत है लेकिन, जाहिर है, यह एक बहुत ही विशिष्ट परीक्षण मामला है।
chown

42

हालांकि यह सवाल पुराना है, चीजें बदल नहीं रही हैं, लेकिन स्वीकृत उत्तर गलत है।

enumerateObjectsUsingBlockएपीआई विषय वस्तु नहीं था for-in, लेकिन एक पूरी तरह से अलग उपयोग स्थिति के लिए:

  • यह मनमानी, गैर-स्थानीय तर्क के आवेदन की अनुमति देता है। यानी आपको यह जानने की जरूरत नहीं है कि ब्लॉक एक सरणी पर इसका उपयोग करने के लिए क्या करता है।
  • बड़े संग्रह या भारी संगणना के लिए समवर्ती गणना ( withOptions:पैरामीटर का उपयोग करके )

फास्ट एन्यूमरेशन for-inअभी भी एक संग्रह की गणना करने का मुहावरेदार तरीका है।

फास्ट एन्यूमरेशन से कोड, पठनीयता और अतिरिक्त अनुकूलन की संक्षिप्तता से लाभ होता है जो इसे अस्वाभाविक रूप से तेज बनाता है। एक पुराने सी के लिए तेजी से लूप!

एक त्वरित परीक्षण का निष्कर्ष है कि iOS 7 पर वर्ष 2014 में, enumerateObjectsUsingBlockफॉर-इन (100 आइटम सरणी के 1 मिमी पुनरावृत्तियों के आधार पर) की तुलना में लगातार 700% धीमा है।

क्या प्रदर्शन यहां वास्तविक व्यावहारिक चिंता है?

निश्चित रूप से दुर्लभ अपवाद के साथ नहीं।

बिंदु प्रदर्शित करने के लिए उपयोग करने के लिए थोड़ा लाभ है कि वहाँ है enumerateObjectsUsingBlock:से अधिक for-inएक बहुत अच्छे कारण के बिना। यह कोड को अधिक पठनीय ... या तेज ... या थ्रेड-सुरक्षित नहीं बनाता है। (एक और आम गलतफहमी)।

पसंद व्यक्तिगत पसंद के लिए नीचे आती है। मेरे लिए, मुहावरेदार और पठनीय विकल्प जीतता है। इस मामले में, यह फास्ट एन्यूमरेशन का उपयोग कर रहा है for-in

बेंचमार्क:

NSMutableArray *arr = [NSMutableArray array];
for (int i = 0; i < 100; i++) {
    arr[i] = [NSString stringWithFormat:@"%d", i];
}
int i;
__block NSUInteger length;

i = 1000 * 1000;
uint64_t a1 = mach_absolute_time();
while (--i > 0) {
    for (NSString *s in arr) {
        length = s.length;
    }
}
NSLog(@"For-in %llu", mach_absolute_time()-a1);

i = 1000 * 1000;
uint64_t b1 = mach_absolute_time();
while (--i > 0) {
    [arr enumerateObjectsUsingBlock:^(NSString *s, NSUInteger idx, BOOL *stop) {
        length = s.length;
    }];
}
NSLog(@"Enum %llu", mach_absolute_time()-b1);

परिणाम:

2014-06-11 14:37:47.717 Test[57483:60b] For-in 1087754062
2014-06-11 14:37:55.492 Test[57483:60b] Enum   7775447746

4
मैं इस बात की पुष्टि कर सकता हूं कि मैकबुक प्रो रेटिना 2014 पर एक ही टेस्ट चलाना, enumerateObjectsUsingBlockवास्तव में 5X धीमा है। यह जाहिरा तौर पर ब्लॉक के प्रत्येक आह्वान को लपेटते हुए एक ऑटोरेलिज पूल के कारण होता है, जो for inमामले के लिए नहीं होता है ।
पोल

2
मैं पुष्टि करता हूं कि enumerateObjectsUsingBlock:असली iPhone 6 iOS9 पर अभी भी 4X धीमा है, निर्माण के लिए Xcode 7.x का उपयोग कर रहा है।
कूर

1
पुष्टि करने के लिए धन्यवाद! काश यह जवाब इतना दफन न होता ... कुछ लोगों को enumerateसिंटैक्स पसंद है क्योंकि यह एफपी-जैसा लगता है और प्रदर्शन विश्लेषण नहीं सुनना चाहता।
एडम कपलान

1
वैसे, यह सिर्फ ऑटोरेलिज पूल के कारण नहीं है। बहुत अधिक स्टैक धक्का और पॉपिंग के साथ हैenumerate:
एडम कपलान

24

प्रदर्शन के बारे में सवाल का जवाब देने के लिए, मैंने अपने प्रदर्शन परीक्षण परियोजना का उपयोग करके कुछ परीक्षण किए । मैं जानना चाहता था कि किसी सरणी में सभी वस्तुओं को संदेश भेजने के लिए तीन विकल्पों में से कौन सा सबसे तेज है।

विकल्प थे:

1) MakeObjectsPerformSelector

[arr makeObjectsPerformSelector:@selector(_stubMethod)];

2) तेज गणना और नियमित संदेश भेजें

for (id item in arr)
{
    [item _stubMethod];
}

3) enumerateObjectsUsingBlock और नियमित संदेश भेजें

[arr enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) 
 {
     [obj _stubMethod];
 }];

यह पता चला है कि makeObjectsPerformSelector अब तक का सबसे धीमा था। यह तेजी से गणना के रूप में दो बार लिया। और enumerateObjectsUsingBlock सबसे तेज था, यह तेज पुनरावृत्ति की तुलना में लगभग 15-20% तेज था।

इसलिए यदि आप सर्वश्रेष्ठ संभावित प्रदर्शन के बारे में बहुत चिंतित हैं, तो enumerateObjectsUsingBlock का उपयोग करें। लेकिन ध्यान रखें कि कुछ मामलों में किसी संग्रह को संकलित करने में लगने वाला समय उस समय तक बौना हो जाता है जब तक कि आप प्रत्येक वस्तु को निष्पादित करने के लिए जो भी कोड चाहते हैं उसे चला लें।


क्या आप अपने विशिष्ट परीक्षण की ओर इशारा कर सकते हैं? लगता है आपने गलत उत्तर दिया है।
C .ur

3

जब आप नेस्टेड लूप को तोड़ना चाहते हैं तो बाहरी लूप के रूप में enumerateObjectsUsingBlock का उपयोग करना काफी उपयोगी है।

जैसे

[array1 enumerateObjectsUsingBlock:^(id obj1, NSUInteger idx, BOOL * _Nonnull stop) {
  for(id obj2 in array2) {
    for(id obj3 in array3) {
      if(condition) {
        // break ALL the loops!
        *stop = YES;
        return;
      }
    }
  }
}];

विकल्प गोटो स्टेटमेंट का उपयोग कर रहा है।


1
या आप सिर्फ उसी विधि से वापस लौट सकते हैं जैसे आपने यहां किया है :-D
एडम कपलान

1

प्रदर्शन पर व्यापक तुलना शुरू करने के लिए @bbum और @Chuck का धन्यवाद। खुशी है कि यह तुच्छ है। मुझे लगता है कि साथ चले गए हैं:

  • for (... in ...)- मेरा डिफ़ॉल्ट गोटो के रूप में। मेरे लिए अधिक सहज, किसी भी वास्तविक वरीयता की तुलना में यहां अधिक प्रोग्रामिंग इतिहास - क्रॉस भाषा का पुन: उपयोग, आईडीई ऑटो पूर्ण होने के कारण अधिकांश डेटा संरचनाओं के लिए कम टाइपिंग: पी।

  • enumerateObject...- जब ऑब्जेक्ट और इंडेक्स तक पहुंच की आवश्यकता हो। और जब गैर-सरणी या शब्दकोश संरचनाओं तक पहुंच (व्यक्तिगत प्राथमिकता)

  • for (int i=idx; i<count; i++) - सरणियों के लिए, जब मुझे गैर-शून्य सूचकांक पर शुरू करने की आवश्यकता होती है

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