15

में उद्देश्य-सी लीकिंग में रिकर्सिव ब्लॉक, तो मैं रिकर्सिव ब्लॉक का उपयोग कर रहा हूं। मैं समझता हूं कि एक ब्लॉक के लिए रिकर्सिव होने के लिए इसे __block कीवर्ड से पहले होना चाहिए, और इसकी प्रतिलिपि बनाई जानी चाहिए ताकि इसे ढेर पर रखा जा सके। हालांकि, जब मैं ऐसा करता हूं, यह इंस्ट्रूमेंट्स में रिसाव के रूप में दिख रहा है। क्या कोई जानता है कि मैं इसके आसपास क्यों या कैसे प्राप्त कर सकता हूं?एआरसी

कृपया नीचे दिए गए कोड में ध्यान दें कि मुझे कई अन्य ब्लॉक के संदर्भ मिल गए हैं, लेकिन उनमें से कोई भी रिकर्सिव नहीं है।

__block NSDecimalNumber *(^ProcessElementStack)(LinkedList *, NSString *) = [^NSDecimalNumber *(LinkedList *cformula, NSString *function){ 
     LinkedList *list = [[LinkedList alloc] init]; 
     NSDictionary *dict; 
     FormulaType type; 
     while (cformula.count > 0) { 
      dict = cformula.pop; 
      type = [[dict objectForKey:@"type"] intValue]; 
      if (type == formulaOperandOpenParen || type == formulaListOperand || type == formulaOpenParen) [list add:ProcessElementStack(cformula, [dict objectForKey:@"name"])]; 
      else if (type == formulaField || type == formulaConstant) [list add:NumberForDict(dict)]; 
      else if (type == formulaOperand) [list add:[dict objectForKey:@"name"]]; 
      else if (type == formulaCloseParen) { 
       if (function){ 
        if ([function isEqualToString:@"AVG("]) return Average(list); 
        if ([function isEqualToString:@"MIN("]) return Minimum(list); 
        if ([function isEqualToString:@"MAX("]) return Maximum(list); 
        if ([function isEqualToString:@"SQRT("]) return SquareRoot(list); 
        if ([function isEqualToString:@"ABS("]) return EvaluateStack(list).absoluteValue; 
        return EvaluateStack(list); 
       } else break; 
      } 
     } 
     return EvaluateStack(list); 
    } copy]; 
    NSDecimalNumber *number = ProcessElementStack([formula copy], nil); 

अद्यतन तो मेरे अपने अनुसंधान के क्षेत्र में मुझे पता चला है समस्या जाहिरा तौर पर अन्य ब्लॉक इस ब्लॉक का उपयोग करता है के संदर्भ के साथ क्या संबंध है कि। मैं कुछ इस तरह सरल करते हैं, यह रिसाव नहीं करता है:

__block void (^LeakingBlock)(int) = [^(int i){ 
     i++; 
     if (i < 100) LeakingBlock(i); 
    } copy]; 
    LeakingBlock(1); 

हालांकि, अगर मैं इस में एक एक और ब्लॉक जोड़ने के लिए, यह रिसाव करता है:

void (^Log)(int) = ^(int i){ 
    NSLog(@"log sub %i", i); 
}; 

__block void (^LeakingBlock)(int) = [^(int i){ 
    Log(i); 
    i++; 
    if (i < 100) LeakingBlock(i); 
} copy]; 
LeakingBlock(1); 

मैं __block का उपयोग कर की कोशिश की है लॉग() के लिए कीवर्ड और इसे कॉपी करने का भी प्रयास किया, लेकिन यह अभी भी लीक है। कोई विचार?

अद्यतन 2 मुझे रिसाव को रोकने के लिए एक रास्ता मिला, लेकिन यह थोड़ा कठिन है। यदि मैं पारित ब्लॉक को एक कमजोर आईडी में परिवर्तित करता हूं, और उसके बाद कमजोर आईडी को ब्लॉक प्रकार में डाल देता हूं, तो मैं रिसाव को रोक सकता हूं।

void (^Log)(int) = ^(int i){ 
    NSLog(@"log sub %i", i); 
}; 

__weak id WeakLogID = Log; 

__block void (^LeakingBlock)(int) = [^(int i){ 
    void (^WeakLog)(int) = WeakLogID; 
    WeakLog(i); 
    if (i < 100) LeakingBlock(++i); 
} copy]; 
LeakingBlock(1); 

निश्चित रूप से एक बेहतर तरीका है?

+0

अपने शोध को साझा करने के लिए धन्यवाद, मैंने ब्लॉक की प्रतिलिपि बनाने के बारे में नहीं सुना है। हालांकि, ऐसा प्रतीत होता है कि एक हालिया एलएलवीएम इस ब्लॉक में दृढ़ता से कॉल "लीकिंगब्लॉक कैप्चरिंग" पर एक चेतावनी को उत्सर्जित करता है, जो एक चक्र को बनाए रखने की संभावना है।संकलक को प्रसन्न करने का एकमात्र तरीका यह है कि ब्लॉक के लिए एक अलग कमजोर पीआरटी का उपयोग कुछ हद तक आपके उत्तर के समान है, हालांकि इसके अनावश्यक रूप से पर्याप्त है कि मैं स्थानीय रूप से चेतावनी को ओवरराइड करने के लिए प्रेरित हूं। एक बार जब आप नवीनतम कंपाइलर आज़माते हैं तो मुझे अपना लेना देखने में दिलचस्पी होगी। –

+0

@smallduck मूल रूप से, मैंने 'प्रतिलिपि' का उपयोग किया क्योंकि यह ब्लॉक को ढेर से ढेर तक कॉपी करने का कारण बनता है। थोड़ी देर के लिए जो ठीक काम करता था और मुझे कंपाइलर "रिकर्सिव" त्रुटि भी मिली। मैंने अपने कोड से 'कॉपी' हटा दी है (जैसा कि मेरे उत्तर में दिखाई देता है) और यह काम करता है (जबकि पहले मुझे 'EXC_BAD_ACCESS' मिल जाएगा। मुझे लगता है कि ऐप्पल ने' __block' कीवर्ड को बदल दिया है ताकि ढेर पर ब्लॉक बना सकें ढेर ... लेकिन यह सिर्फ एक अनुमान है। –

+0

@smallduck सचमुच, मैंने रिकर्सन के लिए ब्लॉक का उपयोग छोड़ दिया है। हाँ, यह किया जा सकता है लेकिन यह थोड़ा अनावश्यक है और बहुत सारे नुकसान हैं। इसे समाप्त करना बहुत आसान है चक्र बनाए रखें (जो रिकर्सन के साथ वास्तव में खराब हो सकता है) और इसे पढ़ना मुश्किल हो जाता है। इसलिए आमतौर पर मैं रिकर्सन करने के लिए विधियों/कार्यों के साथ चिपक जाता हूं। –

उत्तर

11

ठीक है, मैं अपने दम पर जवाब मिला ... लेकिन जो लोग मदद करने की कोशिश करने के लिए धन्यवाद।

यदि आप रिकर्सिव ब्लॉक में अन्य ब्लॉक का संदर्भ/उपयोग कर रहे हैं, तो आपको उन्हें कमजोर चर के रूप में पास करना होगा। बेशक, __weak केवल ब्लॉक सूचक प्रकार पर लागू होता है, इसलिए आपको पहले उन्हें टाइप करना होगा। अंतिम समाधान यहां दिया गया है:

typedef void (^IntBlock)(int); 

    IntBlock __weak Log = ^(int i){ 
     NSLog(@"log sub %i", i); 
    }; 

    __block void (^LeakingBlock)(int) = ^(int i){ 
     Log(i); 
     if (i < 100) LeakingBlock(++i); 
    }; 
    LeakingBlock(1); 

उपरोक्त कोड रिसाव नहीं करता है।

+1

यदि आपको यह नहीं चाहिए तो आपको टाइपिफ़ की आवश्यकता नहीं है: 'शून्य (^ __ कमजोर लॉग) (int) =^(int i) {...};' –

+2

@ मैटविल्डिंग अच्छी पकड़। मुझे लगता है कि एक्सकोड के पिछले संस्करणों में काम करने के लिए इस्तेमाल नहीं किया गया था। लगता है कि संकलक लगातार ब्लॉक के संबंध में बदल रहे हैं। मैंने अभी एक्सकोड 4.6 डाउनलोड किया है और अब यह शिकायत करता है कि उपरोक्त कोड "मई" एक बनाए रखने के चक्र का कारण बन सकता है। मुझे लगता है कि ऐप्पल अभी भी पूरी "ब्लॉक" चीज को समझ रहा है। –

+1

मुझे लगता है कि प्रत्येक कंपाइलर संशोधन के साथ मेरा ब्लॉक कोड बदलना प्रतीत होता है। मेरे पास 4.6 के साथ एक ही चेतावनी थी, और आप 'लीकिंगब्लॉक' को '__weak' मोडिफ़र के साथ चिह्नित करके इसे ठीक कर सकते हैं। –

-1

आगे संदर्भ जानकारी के बिना, मैं यह कह सकता हूँ:

आपको लगता है कि ब्लॉक लीक कर रहे हैं क्योंकि आप इसे कॉपी कर रहे हैं और इसे कहीं और रिहा नहीं। इसे इसे ढेर में ले जाने के लिए इसे कॉपी करने की आवश्यकता है, ठीक है। लेकिन जिस तरह से आपने चुना है वह बिल्कुल ठीक नहीं है।

इसे करने का एक सही तरीका इसे कुछ ऑब्जेक्ट इंस्टेंस वैरिएबल के रूप में स्टोर करना है, इसे कॉपी करें, और फिर इसे डेलोक के अंदर छोड़ दें। कम से कम, यह लीक किए बिना ऐसा करने का एक तरीका है।

+2

मैं इसे मैन्युअल रूप से रिलीज़ नहीं कर सकता। मैं एआरसी (स्वचालित संदर्भ गिनती) का उपयोग कर रहा हूं जो रोकता है मुझे ऐसा करने से। ब्लॉक का दायरा विधि है, इसलिए आईवर का उपयोग कोड गंध है। ब्लॉक को विधि के अंत में जारी किया जाना चाहिए। –

+0

वह एआरसी – Eonil

0

हारून,

अपने कोड के रूप में, एक लड़ी हो गया लगता है तुम क्यों ब्लॉक कॉपी कर रहे हैं? यदि आप ब्लॉक की प्रतिलिपि नहीं बनाते हैं, तो आपके पास रिसाव नहीं है।

एंड्रयू

+0

का उपयोग कर रहा है हाँ आप सही हैं, कोड सिंगल है थ्रेडेड। मुझे यकीन नहीं है मैं यद्यपि उस घोषणा के बिंदु को समझें। यदि मैं ब्लॉक की प्रतिलिपि नहीं बनाता तो मुझे EXC_BAC_ACCESS मिलता है जब ब्लॉक स्वयं को पुनः प्राप्त करने का प्रयास करता है। –

+1

असल में, मुझे स्पष्टीकरण देना चाहिए: ब्लॉक की प्रतिलिपि के बिना, मुझे असाइनमेंट पर EXC_BAD_ACCESS मिलता है, न कि जब ब्लॉक खुद को पुनः एक्सेस करने का प्रयास करता है (ऐसा तब होता है जब मैं __block का उपयोग नहीं करता)। मुझे विवरणों का थोड़ा अनिश्चितता है, लेकिन मुझे विश्वास है कि ऐसा इसलिए है क्योंकि ब्लॉक को शुरुआत में स्टैक पर एक कॉन्स ऑब्जेक्ट के रूप में बनाया गया है, जबकि ब्लॉक के भीतर संदर्भित ब्लॉक एक ही कॉन्स स्टैक ब्लॉक है। यह ब्लॉक को ढेर में पहले कॉपी करके तय किया जाता है, फिर इसे __block var को असाइन किया जाता है, इसलिए यह स्वयं की बजाय एक प्रतिलिपि के बजाय स्वयं की एक प्रति को संदर्भित कर सकता है। –