2011-12-08 10 views
13

कोड की अगली पंक्ति में जाने से पहले मैं अपने प्रोग्राम को एसिंक्रोनस NSURLConnection के लिए कैसे समाप्त कर सकता हूं?अगली कमांड के साथ आगे बढ़ने से पहले प्रोग्राम को एसिंक्रोनस NSURLConnection के लिए कैसे समाप्त करना है?

SetDelegate *sjd= [SetDelegate alloc]; 
NSURLConnection *connection = [[NSURLConnection alloc]initWithRequest:post delegate:sjd]; 
[connection start]; 

इस तरह मैं कनेक्शन शुरू करने और मैं प्रतिनिधि में प्राप्त डेटा को संभाल लेकिन मैं कनेक्शन मुख्य रूप से आगे बढ़ने से पहले समाप्त करने के लिए के लिए, क्योंकि इस पाश के लिए एक में है इंतज़ार करना चाहते हैं और यह प्रत्येक के लिए चलाने के लिए है मेरे डेटाबेस में तत्व।

मुझे फ़ोन डेटाबेस से डेटा को दूरस्थ डेटाबेस में रखना होगा और फ़ोन डेटाबेस में डेटा को सफलतापूर्वक डेटा में डाल दिया जाना चाहिए। मैं फोन के डेटाबेस में प्रत्येक तत्व के माध्यम से जा रहा हूं और एक कनेक्शन शुरू कर रहा हूं इसलिए मैं नहीं देखता कि लूप से अगली चीजें कैसे की जा सकती हैं। जब मैं उद्देश्य-सी प्रोग्रामिंग की बात करता हूं तो मैं एक नौसिखिया हूं इसलिए मुझे यकीन नहीं है कि यह सही तरीका है या नहीं करना

कॉल सिंक्रोनस बनाना एक विकल्प नहीं है क्योंकि यह प्रोग्राम को अवरुद्ध करता है और मेरे पास है एक प्रगति पट्टी जो दिखाना चाहिए।

+1

किसी भी कारण है कि आप कर सकते हैं नहीं बस है कि प्रतिनिधि से बजाय वहीं के पाश में "अगले" सामान? :) –

+0

क्या पैरालेल में कई अनुरोध शुरू करने से बचने का उद्देश्य है?यदि ऐसा है, तो सर्वर से अगले परिणाम लाने के लिए आपकी प्रतिनिधि विधि जिम्मेदार हो सकती है। बहुत कम स्थितियां हैं जहां आप वास्तव में एक HTTP अनुरोध पर अवरुद्ध करना चाहते हैं, खासकर यदि आप उनमें से कई कर रहे हैं। –

+0

मैं ओपी के समान स्थिति में भाग गया, जहां मेरे पास एक ऐप था जो पृष्ठभूमि थ्रेड पर सिंक्रोनस कॉल का भारी इस्तेमाल करता था, और अब कस्टम प्रमाणीकरण (NSURLConnection के प्रतिनिधि विधियों का उपयोग करके) का समर्थन करना चाहता था, जो एसिंक्रोनस उपयोग को अनिवार्य करता है। इसके बजाय ऐप को फिर से लिखना, मैं मूल रूप से उन्हें सिंक्रोनस बनाने के लिए एसिंक्रोनस कॉल को "लपेट" सकता था, क्योंकि वे पृष्ठभूमि थ्रेड पर पहले से ही होते हैं, सब कुछ अच्छा है। – Aaron

उत्तर

19

आपका प्रश्न थोड़ा अजीब है। आपने इस मुद्दे को असंभव रूप से बाधित कर दिया है। किसी भी प्रक्रिया को रोकने के लिए आपके पास "प्रतीक्षा" कोड की एक पंक्ति नहीं हो सकती है, इस मामले में जो भी लूप चल रहा है,

यदि आप चाहते हैं तो आप एक सिंक्रोनस कॉल का उपयोग कर सकते हैं, यह नहीं करता अपने ऐप को अवरुद्ध न करें, यह केवल उस धागे को अवरुद्ध करता है जिस पर इसे निष्पादित किया जाता है। आपके उदाहरण में, आपके पास एक लूप है जो लगातार रिमोट डेटा प्राप्त कर रहा है और आप चाहते हैं कि आपका यूआई प्रतिबिंबित न हो जाए। लेकिन आप नहीं चाहते कि आपका यूआई अवरुद्ध हो। इसका मतलब है कि, आपके लूप के साथ यह धागा पहले से ही पृष्ठभूमि थ्रेड पर होना चाहिए ताकि आप अपने यूआई थ्रेड को अवरुद्ध करने वाले लूप w/o में एक सिंक्रोनस कॉल करने के लिए स्वतंत्र महसूस कर सकें। यदि लूप यूआई थ्रेड पर है तो आपको इसे वही करने के लिए इसे बदलने की जरूरत है।

आप एसिंक्रोनस कनेक्शन का उपयोग करके भी ऐसा कर सकते हैं। उस स्थिति में, आपका ऑपरेशन वास्तविक रूप से तेज़ी से पूरा हो सकता है बी/सी आप एक ही समय में कई अनुरोध प्रगति कर सकते हैं। यदि आप इसे इस तरह करते हैं, तो आपका लूप यूआई थ्रेड पर बना सकता है और आपको बस सभी कनेक्शन ट्रैक करने की आवश्यकता है ताकि जब वे समाप्त हो जाएं तो आप उस स्थिति को प्रासंगिक नियंत्रकों से संवाद कर सकते हैं। लोडिंग स्थिति को ट्रैक करने के लिए आपको iVars की आवश्यकता होगी और लोड होने पर संवाद करने के लिए या तो प्रोटोकॉल या NSNotification का उपयोग करें।

संपादित करें: पृष्ठभूमि धागे पर तुल्यकालिक कॉल का जोड़ा उदाहरण

आप पाश पूरी तरह से केवल खत्म जब सभी अनुरोधों को खत्म कर रहे हैं और न अपने यूआई यहाँ धागा एक सरल उदाहरण है अवरुद्ध करना चाहते हैं:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 
    // post an NSNotification that loading has started 
    for (x = 0; x < numberOfRequests; x++) { 
     // create the NSURLRequest for this loop iteration 
     NSURLResponse *response = nil; 
     NSError *error = nil; 
     NSData *data = [NSURLConnection sendSynchronousRequest:request 
              returningResponse:&response 
                 error:&error]; 
     // do something with the data, response, error, etc 
    } 
    // post an NSNotification that loading is finished 
}); 

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

+0

को नहीं रोकते हैं @pillblast, जिस तरह से आप सभी ऐप्स को करने का प्रयास कर रहे हैं (कुछ डाउनलोड करें और यूआई उत्तरदायी हो और पहले, उसके दौरान और बाद में स्थिति को प्रतिबिंबित करें) यूआई थ्रेड को अनब्लॉक करके और लोडिंग ऑपरेशंस को असीमित रूप से कर रहा है। आप या तो एसिंक्रोनस NSURLConnection या ऊपर दिए गए मेरे उदाहरण से ऐसा कर सकते हैं जो पृष्ठभूमि थ्रेड पर एक सिंक्रोनस अनुरोध करता है। मुख्य (UI) थ्रेड पर सिंक्रोनस ऑपरेशन जैसी कोई चीज नहीं है जो थ्रेड को अवरुद्ध नहीं करती है। – XJones

+0

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

+0

नेटवर्किंग पूरा होने तक यूआई को अवरुद्ध करने के बजाय आपको नीचे दिए गए सुझाव का उपयोग करना चाहिए। यदि आप जेरी सुझावों जैसी सूचनाओं का उपयोग करते हैं, तो आपका यूआई अवरुद्ध नहीं होता है और नेटवर्किंग कनेक्शन समाप्त होने के बाद ही अपडेट होता है। – anders

-1

NSURLConnection पहले से ही असीमित है। बस प्रतिनिधि विधियों को लागू करें। आप MainThread (जैसे एक प्रगति बार) पर यूआई अद्यतन करना चाहते हैं, तो आप didReceiveData. में ऐसा या this

+0

मेरा सवाल यह व्यावहारिक रूप से सिंक्रोनस बनाने के बारे में था इसलिए मुझे आपका जवाब – Pillblast

3

देखो तुम सच में प्रतीक्षा करने के लिए, क्यों सब पर एक अतुल्यकालिक कॉल उपयोग करना चाहते हैं कर सकते हैं? बजाय एक तुल्यकालिक कॉल का उपयोग करें:

NSURLResponse* response = nil; 
NSData* data = [NSURLConnection sendSynchronousRequest:urlRequest returningResponse:&response error:nil] 

यह दृष्टिकोण धागा यह मार डाला है अवरुद्ध कर देगा, तो क्या आप वाकई यह करना चाहते हैं होना चाहिए! क्या मैंने उल्लेख किया कि यह अवरुद्ध होगा? :)

+0

ठीक नहीं समझ रहा है, इसलिए मैं एक सिंक्रोनस कॉल का उपयोग नहीं करना चाहता, क्योंकि यह एप्लिकेशन को अवरुद्ध कर देगा। यह एक विकल्प नहीं है। – Pillblast

+0

तो आप डाउनलोड की एक श्रृंखला चाहते हैं, एक प्रगति संकेतक है, और उपयोगकर्ता को अन्य चीजें करने की अनुमति देता है, सही? एक संभावित एवेन्यू इस पूरे ऑपरेशन को लेना और इसे एनएसओपरेशन में रखना है। क्या आपको प्रत्येक व्यक्तिगत डाउनलोड, या डाउनलोड के सेट की प्रगति दिखाने के लिए प्रगति पट्टी चाहिए? आप कितने आइटम डाउनलोड कर रहे हैं और वे कितने बड़े हैं? –

+0

वे बहुत बड़े नहीं हैं लेकिन वे उनमें से सैकड़ों हो सकते हैं। मुद्दा यह है कि एनएसओपरेशन में भी मुझे एक ही समस्या होगी कि अब मैं लूप के अंदर एसिंक्रोनस एनएसयूआरएलकनेक्शन का उपयोग कर रहा हूं। – Pillblast

1

आप कहते हैं कि आप को पूरा करने के लिए एक अतुल्यकालिक कॉल के लिए इंतजार करना चाहते हैं, तो मैं आपको कोड आप एक अलग थ्रेड में तैनात कॉल कर रहे हैं संभालने हूँ।

मैं नई sendAsynchronourRequest विधि को देखने की अनुशंसा करता हूं। मेरे पास posted up an example of how you can wrap this up in a class है जो कनेक्शन को पूरा/समय समाप्त/विफल होने पर अपने प्रतिनिधि को सूचित करेगा। मैं केवल आपको इस पोस्ट का जिक्र कर रहा हूं क्योंकि ऐसा लगता है कि आप उस समय के समान कुछ हासिल करने की कोशिश कर रहे हैं, और यह DownloadWrapper वर्ग मेरे लिए बेकार ढंग से काम करता था। आईओएस 5 में यह नया है, आपको दिमाग है।

5

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

प्रतिनिधि - पूरा होने पर पोस्ट अधिसूचना

-(void) connectionDidFinishLoading:(NSURLConnection*)connection { 
    [[NSNotificationCenter defaultCenter] postNotificationName:@"NSURLConnectionDidFinish" object:nil]; 
} 

देखें - पर्यवेक्षक जोड़ें और जब

-(void) viewDidLoad { 

[[NSNotificationCenter defaultCenter] addObserver:self 
              selector:@selector(yourCleanupMethod) 
               name:@"NSURLConnectionDidFinish" 
               object:nil]; 
} 

-(void) yourCleanupMethod { 
    // finish up 

    [[NSNotificationCenter defaultCenter] removeObserver:self]; 
} 

अब मनाया कुछ कोड, बना रहेगा यदि आप एक साधारण वस्तु वापस के रूप में पारित करने के लिए की जरूरत है डेटा आप अपनी अधिसूचना में ऑब्जेक्ट पैरामीटर को लोड करने का प्रयास कर सकते हैं:

[[NSNotificationCenter defaultCenter] postNotificationName:@"NSURLConnectionDidFinish" object:yourDataObject]; 

तो इस तरह आपके विचार और सफाई हस्ताक्षर बदलने के लिए:

-(void) viewDidLoad { 

// Notice the addition to yourCleanupMethod 
[[NSNotificationCenter defaultCenter] addObserver:self 
              selector:@selector(yourCleanupMethod:) 
               name:@"NSURLConnectionDidFinish" 
               object:nil]; 
} 

-(void) yourCleanupMethod:(NSNotification *)notif { 
    // finish up 
    id yourDataObject = [notif object]; 

    [[NSNotificationCenter defaultCenter] removeObserver:self]; 
} 

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

मैं उस कोड को यहां शामिल नहीं करूँगा लेकिन हो सकता है कि इससे आपको क्या संभव हो इसके बारे में सोचने में मदद मिलेगी। मैंने पाया कि मेरे पास वेब सेवा कॉल करने में बहुत सारे कोड बंधे थे, इसलिए सिंगलटन में सबकुछ लपेटकर मुझे डेटा प्राप्त करने का एक अच्छा साफ तरीका मिला। उम्मीद है की यह मदद करेगा!

+1

घटनाएं होने पर "कॉल बैक" बनाने के लिए अधिसूचनाएं बहुत उपयोगी होती हैं। मैं इस विधि का सुझाव दूंगा – anders

1

This गोल्डन नगेट ने मेरी मदद की! मैं सिंक्रोनस NSURL का उपयोग तब तक ठीक कर रहा था जब तक कि मैंने फैसला नहीं किया कि मुझे अपने क्लाइंट और मेरे सर्वर के बीच अपने कनेक्शन के लिए SSL की आवश्यकता है। मैंने key pinning का दृष्टिकोण लिया जो सर्वर पर प्रमाण पत्र की तुलना सर्वर पर प्रमाणित कर रहा है (ऊपर दिए गए लिंक पर और पढ़ें) और इसे काम करने के लिए मुझे NSURL विधियों में कोड जोड़ने की आवश्यकता है, जो मेरे शोध से आप कर सकते हैं NSURL तुल्यकालिक के साथ नहीं करते हैं। जब तक मैं इस हास्यास्पद सरल समाधान जो मेरे लिए काम किया पाया:

NSString *connectionRunLoopMode = @"connectionRunLoopMode"; 
NSURLConnection *connection = [[NSURLConnection alloc]initWithRequest:urlRequest delegate:urlConnectionDelegate startImmediately:NO]; 
NSRunLoop *currentRunLoop = [NSRunLoop currentRunLoop]; 
[connection unscheduleFromRunLoop:currentRunLoop forMode:NSDefaultRunLoopMode]; 
[connection scheduleInRunLoop:currentRunLoop forMode:connectionRunLoopMode]; 
[connection start]; 
while ([currentRunLoop runMode:connectionRunLoopMode beforeDate:[NSDate distantFuture]]); 
+0

जब मैं यह – Aaron

+0

@Aaron चलाता हूं तो निम्न पंक्ति एक सिगाबर्ट त्रुटि फेंकता है: एरॉन निम्नलिखित पढ़ें: http://stackoverflow.com/questions/8072135/how-to-track-down-cause-of-sigabrt – ConfusedDeer

+1

मैंने इस कोड को 'NSURLConnection * कनेक्शन = [[NSURLConnection alloc] initWithRequest पर छोटा कर दिया: urlRequest प्रतिनिधि: urlConnectionDelegate]; जबकि (urlConnectionDelegate.flag == 0) [[NSRunLoop currentRunLoop] runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 1]]; ' यह मुझे मेरे तुल्यकालिक कॉल एक अतुल्यकालिक कॉल प्रमाणीकरण प्रतिनिधि समर्थन में बदलने के लिए (एक पृष्ठभूमि धागे पर) की अनुमति दी विधियों को सिंक्रोनस होने के बावजूद ताकि मेरे ऐप को भारी रूप से परिवर्तित न किया जा सके। – Aaron

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

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