2013-02-12 16 views
16

बनाए रखने के बेहतर प्रश्न समझने के लिए, ब्लॉक प्रत्यावर्तन के निम्नलिखित सरलीकृत रूप पर विचार तोड़ने:ब्लॉक प्रत्यावर्तन और चक्र

__block void (^next)(int) = ^(int index) { 
    if (index == 3) { 
     return; 
    } 
    int i = index; 
    next(++i); 
}; 
next(0); 

XCode (एआरसी-सक्षम) ने चेतावनी दी है "कैप्चर 'अगले' दृढ़ता से में इस ब्लॉक है कि एक बनाए रखने चक्र की संभावना "।

सहमत हुए।

प्रश्न 1: बनाए रखने चाहेंगे चक्र सफलतापूर्वक इस फैशन में nil को ब्लॉक खुद की स्थापना, द्वारा तोड़ा जा:

__block void (^next)(int) = ^(int index) { 
    if (index == 3) { 
     next = nil; // break the retain cycle 
     return; 
    } 
    int i = index; 
    next(++i); 
}; 
next(0); 

(नोट: यदि आप अभी भी वही चेतावनी मिलती हैं, लेकिन शायद यह अनचाहे है)

प्रश्न 2: ब्लॉक रिकर्सन का बेहतर कार्यान्वयन क्या होगा?

धन्यवाद।

+0

मुझे किसी भी तरह से __block क्यों घोषित किया गया है? यह कब्जा नहीं किया गया है। –

+0

@Catfish_Man, यह '__block int i' है क्योंकि यह कोड की अगली पंक्ति में संशोधित है। – krisk

+0

यह कोड संभवतः काम नहीं कर सका; अगला संकलित नहीं किया जाता है जब तक कि ब्लॉक संकलित नहीं किया जाता है। इस प्रकार, ब्लॉक के भीतर अगली कॉल दुर्घटनाग्रस्त हो जाएगी। –

उत्तर

4

बरकरार-चक्र-मुक्त रिकर्सिव ब्लॉक निष्पादन को पूरा करने के लिए, आपको दो ब्लॉक संदर्भों का उपयोग करने की आवश्यकता है - एक कमजोर और एक मजबूत। आपके मामले के लिए तो, यह क्या कोड की तरह लग सकता है:

__block __weak void (^weak_next)(int); 
void (^next)(int); 
weak_next = next = ^(int index) { 
    if (index == 3) { 
    return; 
    } 
    int i = index; 
    weak_next(++i); 
}; 
next(0); 

ध्यान दें कि ब्लॉक कमजोर ब्लॉक संदर्भ (weak_next) कैप्चर करता है, और बाहरी संदर्भ मजबूत संदर्भ कब्जा (अगले) के आसपास ब्लॉक रखने के लिए । दोनों संदर्भ एक ही ब्लॉक को इंगित करते हैं।

इस पैटर्न के एक अन्य उदाहरण के लिए https://stackoverflow.com/a/19905407/1956124 देखें, जो ब्लॉक रिकर्सन का भी उपयोग करता है। इसके अलावा, निम्नलिखित आलेख के टिप्पणी अनुभाग में चर्चा यहां भी प्रासंगिक है: http://ddeville.me/2011/10/recursive-blocks-objc/

1

मुझे लगता है कि @Newacct @Matt Wilding के समाधान के बारे में सही है; ऐसा लगता है कि उस मामले में अगले ब्लॉक के लिए कुछ भी मजबूत रेफरी नहीं होगी और इसके परिणामस्वरूप रन चलाने पर रन टाइम अपवाद होगा (कम से कम यह मेरे लिए किया गया था)।

मुझे नहीं पता कि ओबीजेसी में जंगली में रिकर्सिव ब्लॉक कहा जाता है। हालांकि, कहते हैं पर, एक दृश्य नियंत्रक एक वास्तविक दुनिया कार्यान्वयन (यदि वास्तव में आवश्यक) में, एक ब्लॉक परिभाषित कर सकता है और उसके बाद के लिए एक मजबूत संदर्भ के साथ एक आंतरिक इंटरफ़ेस प्रॉपर्टी सेट अप कहा ब्लॉक:

typedef void(^PushButtonBlock)(); 

@interface ViewController() 
@property (strong, nonatomic) PushButtonBlock pushButton; 
@end 

@implementation ViewController 
    ... 
    // (in viewDidLoad or some such) 
    __weak ViewController *weakSelf = self; 

    self.pushButton = ^() { 
    [weakSelf.button pushIt]; 
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), weakSelf.pushButton); 
    }; 

    self.pushButton(); 
    ... 
@end 

यह ठीक चलाता है मेरे लिए और चक्र बनाए रखने के बारे में कोई संकलक चेतावनी नहीं है (और उपकरणों में कोई रिसाव नहीं है)। लेकिन, मुझे लगता है कि मैं शायद ओबीजेसी में ज्यादातर मामलों में यह (रिकर्सिव ब्लॉक कॉल) करने से स्पष्ट हो जाऊंगा - यह बदबूदार है। लेकिन किसी भी मामले में दिलचस्प है।

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