2014-09-08 9 views
8

मैं स्मृति मेरी कोड में लीक के साथ एक समस्या हो रहा है, मैं प्रत्येक के लिए पिछले प्राप्त करने का परिणाम से प्रभावित होता है जल्दी जल्दी में कई यूआरएल पाने के लिए एक की जरूरत है,। इसका उद्देश्य प्रतिक्रिया के भीतर सामग्री के एक विशिष्ट टुकड़े को देखना है।रिकर्सिव/Iterative NSURLSessionDataTask के कारण स्मृति रिसाव

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

मेरे दिमाग में ऐसा लगता है कि NSURLSession एपीआई इस स्मृति को लीक करने के लिए ज़िम्मेदार है, और यह तब होता है जब बहुत जल्दी उत्तराधिकार में एकाधिक कॉल किए जाते हैं। हालांकि, अगर मैं कोई भी स्पष्ट गलतियों को इंगित कर सकता हूं तो मैं सराहना करता हूं।

अद्यतन 10/09/14:

एक प्रत्यावर्तन काउंटर जोड़ने के लिए, प्रदर्शन भले ही कोड के समय की एक अनंत संख्या निष्पादित नहीं है रिसाव अब भी होता अपडेट किया गया। दृश्य नियंत्रक के भीतर गुणों के रूप में NSURLSession और NSURLSessionConfiguration का पुन: उपयोग करने के लिए थोड़ा सा कार्यान्वयन भी किया गया।

नमूना कोड:

- (void)performURLCallRecursive { 

    recursionLimiter++; 

    if (recursionLimiter > 10) { 
     [self.session finishTasksAndInvalidate]; 
     return; 
    } 

    NSURL * checkURL = [NSURL URLWithString:@"http://www.google.com"]; 

    __block NSMutableURLRequest * urlRequest = [[NSMutableURLRequest alloc] initWithURL:checkURL 
                      cachePolicy:NSURLRequestReloadIgnoringLocalCacheData 
                     timeoutInterval:0.0f]; 

    __weak typeof(self) weakSelf = self; 

    NSURLSessionDataTask * task = [self.session dataTaskWithRequest:urlRequest 
                completionHandler:^(NSData *data, NSURLResponse *response, NSError 
*error) { 

                 NSString * body = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; 
                 NSLog(@"Body: %@", body); 

                 [weakSelf performURLCallRecursive]; 

                }]; 

    [task resume]; 
} 

#pragma mark - Getters 

- (NSURLSessionConfiguration *)sessionConfiguration { 

    if (!_sessionConfiguration) { 

     _sessionConfiguration = [NSURLSessionConfiguration ephemeralSessionConfiguration]; 

     [_sessionConfiguration setAllowsCellularAccess:NO]; 
     [_sessionConfiguration setTimeoutIntervalForRequest:10.0f]; 
     [_sessionConfiguration setTimeoutIntervalForResource:10.0f]; 

     [_sessionConfiguration setURLCache:[[NSURLCache alloc] initWithMemoryCapacity:0 diskCapacity:0 diskPath:nil]]; 

    } 

    return _sessionConfiguration; 
} 

- (NSURLSession *)session { 

    if (_session == nil) { 

     _session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration 
               delegate:[SPRSessionDelegate new] 
              delegateQueue:nil]; 

    } 

    return _session; 
} 

मेमोरी लीक के रूप में उपकरणों के द्वारा की सूचना दी। (ध्यान दें: ये थोड़ा हर बार अलग-अलग हो, लेकिन अधिकांश भाग के लिए बस और अधिक या कम एक ही लीक एक ही लीक होते हैं,):

Image showing instruments reporting memory leaks

इसके अलावा अद्यतन: तो

, मैं वास्तव में वास्तव में एक ही कोड लागू किया, और स्मृति रिसाव अभी भी होता है। इस उदाहरण के लिए मैंने एक लूप लिमिटर शामिल किया है, इसलिए यह हमेशा के लिए निष्पादित नहीं होता है। क्या कोई मुझे यह जानने में मदद कर सकता है कि पृथ्वी पर क्या चल रहा है?

- (void)performURLCallIterative 
{ 

    int loopLimiter = 0; 

    do { 

     NSURLSessionConfiguration * defaultSession = [NSURLSessionConfiguration defaultSessionConfiguration]; 

     [defaultSession setAllowsCellularAccess:NO]; 
     [defaultSession setTimeoutIntervalForRequest:10.0f]; 
     [defaultSession setTimeoutIntervalForResource:10.0f]; 

     NSURLSession * session = [NSURLSession sessionWithConfiguration:defaultSession 
                   delegate:self 
                  delegateQueue:nil]; 


     NSURL * checkURL = [NSURL URLWithString:@"http://google.com"]; 

     NSMutableURLRequest * urlRequest = [[NSMutableURLRequest alloc] initWithURL:checkURL 
                     cachePolicy:NSURLRequestReloadIgnoringLocalCacheData 
                    timeoutInterval:0.0f]; 

     __weak NSURLSession * weakSession = session; 

     dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); 

     NSURLSessionDataTask * task = [session dataTaskWithRequest:urlRequest 
               completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { 

                NSString * body = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; 
                NSLog(@"Body: %@", body); 

                dispatch_semaphore_signal(semaphore); 

                [weakSession invalidateAndCancel]; 

               }]; 

     [task resume]; 

     dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); 

     loopLimiter++; 

    } while (loopLimiter <= 6); 

} 

अद्यतन 10/09/14:

यह अभी तक किसी भी Googlers के जो उनके रास्ते यहां पाया हो सकता है के लिए आईओएस 8 पर हो रहा है। जहां तक ​​मुझे चिंतित है यह आईओएस में एक बग है।

+0

क्या आप एआरसी या एमआरसी का उपयोग करते हैं? –

+0

प्रोजेक्ट एआरसी – Sammio2

+0

का उपयोग करता है क्या यह वास्तव में आईओएस 8 पर हल किया गया था? मुझे बिल्कुल वही समस्या है। क्या यह अब आपके लिए काम कर रहा है? –

उत्तर

-1

केवल सुझाव मैं शायद एक @autoreleasepool{} का उपयोग कर रहे है और __weak id self__block id self को बदलने। मुझे नहीं लगता कि __block बनाम __weak कुछ भी अलग करेगा, लेकिन इसे एक शॉट दें।

मुझे यकीन नहीं है कि एआरसी के साथ एसिंक्रनाइज़ और कुछ बार चलने पर क्या उम्मीद करनी चाहिए। एसिंक्रोनस रिकर्सिव कॉल और एआरसी के साथ अन्य प्रश्नों को देखते हुए, कोई लगातार समाधान नहीं है। Take a look here, for example.

+1

'__weak' यहां सही है। – orkoden

+0

आपके उत्तर के लिए धन्यवाद, '__weak' से '__block' को स्वैप करने की अपेक्षा कुछ भी नहीं है, और मैं @orkoden से सहमत हूं कि' __weak' यहां जाने का तरीका है। अन्य उत्तरों पूरी तरह से रिकर्सिव ब्लॉक के चारों ओर घूमते प्रतीत होते हैं, मेरा उदाहरण * वास्तव में * रिकर्सन नहीं है क्योंकि मैं ब्लॉक के भीतर से ब्लॉक की मूल विधि को कॉल कर रहा हूं। – Sammio2

4

- अद्यतन 9/12/2014

समाधान: iOS8 के लिए प्रतीक्षा करें।

- अद्यतन 9/10/2014

वाह, इस जटिलता के कुछ वां आयाम में वृद्धि कर रहा है: पी। मुझे उम्मीद है कि एक तरफ या दूसरा आपको जल्दी तोड़ देगा।

आपके पास कोशिश करने के लिए आपके पास कुछ और चीजें हैं।

1) क्या आप वाकई NSZombies कर सकता है बंद कर दिया है। एक्सकोड में, उत्पाद-> योजना-> योजना संपादित करें ...-> ज़ोंबी ऑब्जेक्ट्स सक्षम करें (नहीं टिके हुए)।

2) इसके अलावा अपने NSMutableURLRequest के लिए cachePolicy:NSURLCacheStorageNotAllowed प्रयास करें।

3) जैसा कि आप देख सकता है अगर आप एक त्रुटि के साथ पूरा कर रहे हैं? बस अपने शरीर स्ट्रिंग काम के आसपास इस डाल ...

if (error == nil) 
{ 
    //Enter data->string code here 
} 

4) जैसा कि आप देख सकता है अगर आप स्थिति 200 नहीं मिल रहा है?

NSInteger statusCode = [(NSHTTPURLResponse *)response statusCode]; 

5) यह पता लगाना मुश्किल है कि आपकी परियोजना कैसे स्थापित की गई है। मेरे पास एनएसओब्जेक्ट टाइप क्लास होगा जिसमें NSURLSession विधियां होंगी, जो UIViewController क्लास से अलग है, जहां से इसे कहा जा रहा है। टाइमर या जो भी रिकर्सन विधि आप चुनना चाहते हैं, उसके बाद UIViewController से यूआरएल सत्र संबंधित तरीकों को कॉल करें।

- अद्यतन 9/9/2014

आप मेरे सवाल के बारे में सही कर रहे हैं (2)। डेटा कार्य पूरा होने से पहले फिर से शुरू हो जाता है और डेटा कार्य पूरा होने के बाद सत्र को अमान्य कर दिया जाता है। मैंने इसे इस तरह से नहीं देखा है, लेकिन यह समझ में आता है। बस मेरे अंत पर परीक्षण किया है, के लिए [सत्र invalidateAndCancel] संबंध के साथ कोई लीक ...

आप जाँच कर सकते हैं कि आपके पूरा होने हैंडलर कार्यान्वित? संभवतः ऐसा नहीं होता है और एक नया कार्य शुरू होने से पहले सत्र कभी रद्द नहीं होता है?

मुझे लगता है कि इंस्ट्रूमेंट्स लीक्स रिपोर्ट में HTTP हेडर के कुछ संदर्भ हैं, हो सकता है कि यदि आप या तो [urlRequest setHTTPMethod: @ "GET" निर्दिष्ट नहीं कर रहे हैं] अनुरोध में कुछ मूल शीर्षलेख गुम हैं?

(हम समाधान खोजने के बाद मैं संपादित करेंगे, तो यह एक चर्चा की तरह नहीं दिखता)।

- मूल 9/8/2014

दिलचस्प सवाल! मुझे NSURL सत्रों से जुड़े परेशानी लीक हैं। निश्चित रूप से @autoreleasepool {} और अन्य अब तक प्रयास करने के लिए अच्छे सुझाव हैं ... लेकिन!

मुझे डर है कि बात यह है कि आप हमें कहा पिछले देखने के लिए अपराधी यहाँ हो सकता है कर रहा हूँ।

बस कुछ ही पहले टिप्पणियों:

1) यह मेरे लिए स्पष्ट तुम यहाँ क्यों स्वयं __weak करने की आवश्यकता होगी नहीं है। आप जिस जीवित चक्र से बचने की कोशिश कर रहे हैं वह क्या है? शायद यह उस कोड में अधिक स्पष्ट है जिसे आप वास्तव में अपने "नमूना" से अलग कर रहे हैं।

2) उस सत्र से जुड़े डेटा कार्य से पहले सत्र को अमान्य करने का कारण क्या है, इसे पूरा करने का मौका भी है, अकेले फिर से शुरू करें।डेटा कार्य फिर से शुरू होने तक निलंबित राज्य में है।

3) यदि आप इस तरह की विधि को फिर से चला रहे हैं, तो मुझे लगता है कि निर्दिष्ट करने के लिए यह महत्वपूर्ण है या कम से कम प्रतिनिधि कतार पर विचार करें, अन्यथा यह इसे सीरियल ऑपरेशन कतार में डिफ़ॉल्ट रूप से सेट करने के लिए सेट है। क्या होता है जब प्रतिनिधि अंतराल से पहले कॉल करता है हैंडलर एक अनंत लूप में खत्म होता है - सबसे अधिक संभावना है कि एक बड़ा ढेर हो।

-

मुझे विश्वास है कि यहां मुख्य मुद्दा है कि आप एक नया शुरू करने या NSURLSessionDataTask रद्द करने से पहले इसे पूरा करने के लिए एक मौका है कर रहे हैं। + SesssionWithConfiguration को देखो:

(खेद अभी तक चित्रों को शामिल नहीं कर सकते हैं, उम्मीद है कि इस जवाब के बाद)

https://developer.apple.com/library/ios/documentation/Foundation/Reference/NSURLSession_class/Introduction/Introduction.html#//apple_ref/occ/clm/NSURLSession/sessionWithConfiguration:

यहां मुद्दा यह है ...

महत्वपूर्ण

सत्र ऑब्जेक्ट प्रतिनिधि के लिए एक मजबूत संदर्भ रखता है जब तक कि आपके ऐप का पता नहीं चलता सत्र को अवैध रूप से अमान्य करता है। यदि आप अमान्य और कंसल या रीसेट विथ कॉम्प्लेशन हैंडलर को कॉल करके सत्र को अमान्य करते हैं: विधि, आपका ऐप स्मृति को रिसाव करता है।

मेरे सुझाव कोशिश करने के लिए है ...

//Your code above... 
    [task resume]; 
    [session finishTasksAndInvalidate]; 
} 

सिद्धांत रूप में यह पूरा होने से पहले शुरू करने से किसी भी नए सत्र रोकने चाहिए, विवरण, "... नए कार्य में नहीं बनाया जा सकता के अनुसार सत्र, लेकिन मौजूदा कार्य पूरा होने तक जारी रहते हैं। आखिरी कार्य खत्म होने के बाद और सत्र अंतिम प्रतिनिधि कॉल करता है, प्रतिनिधि और कॉलबैक ऑब्जेक्ट्स के संदर्भ टूट जाते हैं ... "

मुझे अभी भी सत्र को अमान्य करने के बारे में निश्चित नहीं है इसे फिर से शुरू करना

मुझे उम्मीद है कि इससे मदद मिलती है। सौभाग्य।

+0

आपके विस्तृत उत्तर के लिए धन्यवाद, मैं वास्तव में इसके चारों ओर अपना सिर नहीं प्राप्त कर सकता।जिस चीज से मैंने आपको अतीत को देखने के लिए कहा था वह यह तथ्य था कि यह हमेशा के लिए निष्पादित होगा। वास्तविक कार्यान्वयन में मेरे पास यह सुनिश्चित करने के लिए सीमित है कि यह केवल आपके अंक के संबंध में 6 गुना (यह अभी भी लीक हो जाएगा) निष्पादित करेगा। 1) ब्लॉक में दृढ़ता से संदर्भित करने से एक सतत चक्र हो सकता है (देखें: http://bit.ly/1xChWUP) 2) समापन हैंडलर ब्लॉक के * अंत * पर सत्र को अमान्य कर दिया गया है, जिसका अर्थ है कि कार्य निष्पादन समाप्त हो गया है , '[कार्य फिर से शुरू करें]' पहले कहा जाता है। 3) मैंने एक प्रतिनिधि कतार निर्दिष्ट करने का प्रयास किया, अभी भी – Sammio2

+0

लीक एक पुनरावर्तक कार्यान्वयन का उपयोग कर प्रश्न में आगे नमूना कोड जोड़ा गया जो अभी भी लीक है। – Sammio2

+0

__weak स्वयं के बारे में अपना एसओ लिंक पढ़ें। मुझे अभी भी ऐसा करने की कोई ज़रूरत नहीं है, आपके पास स्वयं या ब्लॉक के लिए स्पष्ट या स्पष्ट मजबूत संदर्भ नहीं है। किसी भी आगे निष्पादन होने से पहले एक __weak स्वयं बनाना स्वयं = nil का कारण बन सकता है। –

3

ऐप्पल को एक डेवलपर समर्थन अनुरोध यह आईओएस 7 के भीतर एक बग होने का खुलासा करता है। उपरोक्त कोड नमूना (या तो पुनरावर्ती या पुनरावृत्ति) के साथ कोई गलती नहीं है और यह आईओएस 8 जीएम रिलीज में तय किया गया है।

अद्यतन:

यह अभी भी आईओएस 8.1

+0

बहुत बढ़िया है कि आपने इसे हल किया आदमी! अजीब बग अच्छा काम कर रहा हूँ। आईओएस 8 में शुभकामनाएं। –

+0

दुर्भाग्यवश, अभी भी आईओएस 9 में हो रहा है। :(क्या कोई रडार मैं डुप्ली कर सकता हूं? –

1

में उत्पन्न हो रही है मैं NSURLSession से स्मृति के साथ समस्याओं का एक बहुत कुछ था और मैं अंत में प्रत्येक अनुरोध के लिए एक नया सत्र का उपयोग नहीं करके उसे ठीक किया। सत्र आम तौर पर के रूप में विकिपीडिया पर परिभाषित कर रहे हैं:

एक अर्द्ध स्थायी इंटरैक्टिव सूचना आदान

जैसे, एप्पल के सुविधा वर्ग विधि [NSURLSession sharedSession] हमें कैसे NSURLSession वस्तुओं का इरादा कर रहे हैं की एक संकेत देता है इस्तेमाल किया जा रहा: के रूप में अर्ध-स्थाई वस्तुएं, प्रत्येक अनुरोध के लिए ताजा बनाई गई वस्तुओं को नहीं, जैसे आप कर रहे हैं।

आप एक नए सत्र ऑब्जेक्ट प्रति अनुरोध अनुरोध के अनुरोध के लिए अनुरोध कर रहे हैं, सर्वर के परिप्रेक्ष्य से, एक ही ग्राहक के साथ एक सत्र का हिस्सा हैं।

मैं वही काम कर रहा था जब तक मुझे एहसास हुआ कि यह मेरी समस्याओं का स्रोत था। मुझे इस पर स्पष्ट रूप से ऐप्पल का दस्तावेज नहीं मिला, लेकिन मुझे अपने तरीकों की गलती का एहसास हुआ, इसने प्रलेखन में कुछ चीजें अचानक और अधिक समझ में आईं, जैसे sharedSession NSURL सत्र की सिंगलटन सुविधा विधि क्यों है, शब्द "कार्य क्यों "बहुवचनfinishTasksAndInvalidate में, उन्होंने इसे" सत्र "क्यों कहा, इसका कैश क्यों है, आदि (यदि यह सिर्फ एक अनुरोध के लिए था, तो यह" सत्र "क्यों होगा और" कैश " हो?)

यह जानने में मदद करता है कि सफारी जैसे ब्राउज़र सत्र में कैसे दिखते हैं। पहली बार जब आप किसी दिए गए सर्वर से कनेक्शन बनाते हैं तो एक नया सत्र शुरू होता है। सत्र की स्थापना में एसएसएल प्रमाणपत्रों का कैश बनाना, प्रमाणीकरण, हैंडशेकिंग आदि स्थापित करना शामिल है। यह हर समय ऐसा करने के लिए असाधारण रूप से अक्षम होगा जब किसी पृष्ठ पर कुछ जावास्क्रिप्ट एक ही सर्वर पर एक नया अनुरोध करता है, खासकर आधुनिक वेब ऐप्स के बाद से लगातार कॉलबैक इत्यादि के साथ अनुरोध करते हैं। यही कारण है कि अनुरोधों और प्रतिक्रियाओं के पूरे सेट के लिए एक सत्र स्थापित किया गया है - बातचीत, यदि आप क्लाइंट और सर्वर के बीच करेंगे। आखिरकार, एक सत्र समाप्त हो जाता है, लेकिन आमतौर पर यह कई मिनटों के बाद होता है, के बाद एक अनुरोध!

बिंदु यह है कि, आपको NSURLSession ऑब्जेक्ट्स का उपयोग कैसे करना चाहिए, एक सिंगलटन को दृढ़ता से संदर्भित NSURLSession ऑब्जेक्ट को संपत्ति के रूप में बनाना है। ऐसा करें यदि आपको सत्र की कॉन्फ़िगरेशन को कस्टमाइज़ करने की आवश्यकता है, (जैसे कैशिंग बंद करना आदि)। हालांकि अगर आपको इसे अनुकूलित करने की आवश्यकता नहीं है, तो बस ऐप्पल के sharedSession का उपयोग करें।

यदि आप कस्टम क्लास पर सिंगलटन का उपयोग करते हैं, तो, यदि आपको सत्र संपत्ति को शून्य पर सेट करने की आवश्यकता नहीं है, तो आपको कभी भी invalidateAndCancel या finishTasksAndInvalidate की आवश्यकता नहीं है। इसके बजाय, समय-समय पर कनेक्शन कैश को साफ़ करने के लिए resetWithCompletionBlock या flushWithCompletionBlock

यदि आप सिंगलेट्स से नफरत करते हैं तो भी आप एक संपत्ति के रूप में सत्र का उपयोग कर सकते हैं, बस एआरसी रनटाइम द्वारा अपने अंतिम मालिक को हटाए जाने से पहले invalidateAndCancel या finishTasksAndInvalidate सत्र सुनिश्चित करें।

यह भी ध्यान रखें कि URLCache संपत्ति को nil पर सेट करना कैशिंग बंद करने का उचित तरीका है। ऐप्पल का कहना है कि वे backgroundSessionConfiguration के लिए करते हैं।

इस विषय पर मेरे अन्य उत्तरों देखें here और here

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