2011-03-26 5 views
16

निम्नलिखित कोड पर विचार करें:एक ब्लॉक शाब्दिक संदर्भित संपर्क करना चाहिए ढेर-आवंटित ब्लॉकों

// t included so block1 is a stack block. See [1] below 
int t = 1; 
SimpleBlock block1 = ^{ NSLog(@"block1, %d", t); }; 

// copy block1 to the heap 
SimpleBlock block1_copied = [block1 copy]; 

// block2 is allocated on the stack, and refers to 
// block1 on the stack and block1_copied on the heap 
SimpleBlock block2 = ^{ 
    NSLog(@"block2"); 
    block1_copied(); 
    block1(); 
}; 
[block1_copied release]; 

// When the next line of code is executed, block2_copied is 
// allocated at the same memory address on on the heap as 
// block1_copied, indicating that block1_copied has been 
// deallocated. Why didn't block2 retain block1_copied? 

SimpleBlock block2_copied = [block2 copy]; 
block2_copied(); 
[block2_copied release]; 

कहाँ, पूर्णता के लिए, SimpleBlock द्वारा परिभाषित किया गया है:

typedef void (^SimpleBlock)(void); 

कोड में टिप्पणी ने संकेत के रूप में , मेरे परीक्षण (जीसीसी 4.2 और एलएलवीएम 2.0 दोनों का उपयोग करके) दिखाते हैं कि ब्लॉक 1_copied उस समय तक अवरुद्ध है [ब्लॉक 2 प्रतिलिपि] कहा जाता है, फिर भी मैंने जो दस्तावेज पढ़ा है [1,3], ब्लॉक उद्देश्य-सी ऑब्जेक्ट्स और ब्लॉक हैं उद्देश्य-सी ऑब्जेक्ट्स को बनाए रखें जिनके लिए वे संदर्भित करते हैं [2] (गैर-आवृत्ति परिवर्तनीय सी में ase)।

इसके अतिरिक्त, ध्यान दें कि जब ब्लॉक 2 की प्रतिलिपि बनाई जाती है, तो ब्लॉक 1 का संदर्भ ब्लॉक 1 की एक नई प्रति (जिसे ब्लॉक 1_copied से अलग है) के संदर्भ में भी बदला जाता है, क्योंकि ब्लॉक किसी भी ब्लॉक को कॉपी करते हैं [ 2]।

तो, यहाँ क्या हो रहा है?

ए) ब्लॉक उद्देश्य-सी वस्तुओं पर वे उल्लेख करने के लिए बनाए रखने और ब्लॉक उद्देश्य-सी वस्तुओं रहे हैं, तो क्यों block2 से पहले पुनः आवंटित की जाती block1_copied है दायरे से बाहर चला जाता है?

बी) यदि ब्लॉक ब्लॉक की प्रतिलिपि बनाते हैं, और यदि भेजते हैं - (आईडी) एक ढेर-आवंटित ब्लॉक में प्रतिलिपि बनाते हैं तो वास्तव में इसकी बरकरार संख्या में वृद्धि होती है, ब्लॉक 2 से पहले ब्लॉक 2_copied क्यों हटाया जाता है?

सी) यदि यह अपेक्षित व्यवहार है, जहां प्रलेखन है कि यह बताता है?

[1] http://cocoawithlove.com/2009/10/how-blocks-are-implemented-and.html
[2] http://developer.apple.com/library/ios/#documentation/cocoa/Conceptual/Blocks/Articles/bxVariables.html
[3] http://clang.llvm.org/docs/BlockLanguageSpec.txt

पाद-टिप्पणी: मेरी परीक्षणों में इस कोड को चलाने का परिणाम एक असीम पुनरावर्ती block2_copied लिए() कॉल, block1_copied के बाद से (है) ब्लॉक 2_copied के रूप में एक ही स्मृति पता था।

+1

+1 दिलचस्प .... –

उत्तर

5

This is the specification। यह अभी थोड़ा सा है और इसमें सामान्य स्पेस का औपचारिकता नहीं है। हालांकि, सी कार्यकारी समूह में ब्लॉक प्रस्तावित किए गए हैं और उस संदर्भ में अधिक औपचारिक विनिर्देश पर चर्चा की गई है।

विशेष रूप से, कल्पना का कहना है:

Block_copy ऑपरेटर सभी वस्तुओं स्वत: भंडारण की चर ब्लॉक अभिव्यक्ति के भीतर संदर्भित में आयोजित बरकरार रखती है (या मजबूत संदर्भ के रूप में यदि कचरा संग्रहण के तहत चल रहा)। __block स्टोरेज प्रकार के ऑब्जेक्ट वेरिएबल को पॉइंटर्स रखने के लिए माना जाता है, जिसमें बनाए रखने के लिए कोई प्रावधान नहीं है और संदेश जारी किए जाते हैं।

इस प्रकार, जो व्यवहार आप देख रहे हैं वह सही है, हालांकि यह निश्चित रूप से एक गड़बड़ है!

ब्लॉक की प्रतिलिपि तक एक ब्लॉक कुछ भी नहीं बनाएगा। ढेर पर शुरू होने वाले ब्लॉक की तरह, यह काफी हद तक एक प्रदर्शन आधारित निर्णय है।

आप करने के लिए अपने कोड को बदलने के लिए थे, तो:

SimpleBlock block2_copied = [block2 copy]; 
[block1_copied release]; 

यह उम्मीद के रूप में व्यवहार करता है।

स्थिर विश्लेषक को पकड़ना चाहिए, लेकिन नहीं (कृपया file a bug)।

+0

उत्तर के लिए धन्यवाद। मैंने अभी स्थिर विश्लेषक पर एक बग दायर की है। मुझे लगता है कि यह "डेवलपर प्रोग्रामिंग टॉपिक्स"> "ब्लॉक और वेरिएबल्स"> "ऑब्जेक्ट एंड ब्लॉक वैरिएबल"> "ऑब्जेक्टिव-सी ऑब्जेक्ट्स" से टेक्स्ट दिए गए हैं, यह एक डेवलपर दस्तावेज गलतता है। यहां कोई भी पाठ इंगित करता है कि बनाए रखने के लिए केवल _ होता है _ ब्लॉक की प्रतिलिपि बनाई जाती है। मैं डेवलपर दस्तावेज पर एक दूसरी बग फाइल करूंगा। –

1

मैं ध्यान दें एक ही सामान्य वस्तुओं के साथ हो रहा है। यह कोड:

NSNumber *foo = [[NSNumber alloc] initWithInt:42]; 
void(^block)(void) = ^{ NSLog(@"foo = %@", foo); }; 
[foo release]; 
NSNumber *foo2 = [[NSNumber alloc] initWithInt:43]; 
void(^block_copy)(void) = [block copy]; 
block_copy(); 

प्रिंट "foo = 43"

यह पराक्रम व्यवहार की उम्मीद की जा। एप्पल के प्रलेखन के शब्दों में:

आप एक ब्लॉक को कॉपी करते हैं, कि ब्लॉक के भीतर से अन्य ब्लॉकों के सभी संदर्भ कॉपी कर रहे हैं, यदि आवश्यक हो

बिंदु block1_copy पर जारी की है, block2 अभी तक कॉपी नहीं किया गया है ।

+0

यह निश्चित रूप से सच है। तो यह अपेक्षित व्यवहार हो सकता है। संदर्भ [2] यह भी कहता है "संदर्भ-गणना वाले वातावरण में, डिफ़ॉल्ट रूप से जब आप किसी ब्लॉक के भीतर किसी ऑब्जेक्टिव-सी ऑब्जेक्ट को संदर्भित करते हैं, तो इसे बनाए रखा जाता है। यह सत्य है भले ही आप ऑब्जेक्ट के इंस्टेंस वैरिएबल को संदर्भित करते हैं। ऑब्जेक्ट वेरिएबल्स चिह्नित '__block' स्टोरेज प्रकार संशोधक के साथ, हालांकि, बनाए रखा नहीं है।" यह आवश्यक होने की प्रतिलिपि बनाने के बारे में कुछ भी नहीं कहता है, हालांकि इन उदाहरणों को दिया जा सकता है। बाद में वे उल्लेख करते हैं कि यदि आवश्यक हो तो संदर्भित _blocks_ की प्रतिलिपि बनाई गई है। ऐसा लगता है कि वे अलग-अलग मुद्दे हो सकते हैं। –

+0

@AndrewHershberger: True। बड़ी समस्या यह है कि वास्तविक विनिर्देश नहीं है। – Anomie

+0

@anomie, आप इस स्निपेट में कहीं भी foo2 का संदर्भ नहीं लेते हैं, फिर भी आप दावा करते हैं कि आउटपुट foo2 का मान है। यह संभव ही कैसे है? –

संबंधित मुद्दे