2010-04-25 21 views
9

मैं वेबसाइट- xml से डेटा प्राप्त करने का प्रयास कर रहा हूं। सब कुछ ठीक काम करता है।Async कॉल उद्देश्य सी आईफोन

लेकिन UIButton तब तक दबाया जाता है जब तक कि XML डेटा वापस नहीं किया जाता है और इस प्रकार यदि इंटरनेट सेवा में कोई समस्या है, तो इसे ठीक नहीं किया जा सकता है और ऐप लगभग अनुपयोगी है।

यहां कॉल कर रहे हैं:

{ 
    AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate]; 
    if(!appDelegate.XMLdataArray.count > 0){ 
     [UIApplication sharedApplication].networkActivityIndicatorVisible = YES; 
     [appDelegate GetApps]; //function that retrieves data from Website and puts into the array - XMLdataArray. 

    } 
    XMLViewController *controller = [[XMLViewController alloc] initWithNibName:@"MedGearsApps" bundle:nil]; 
    [self.navigationController pushViewController:controller animated:YES]; 
    [controller release]; 
} 

यह ठीक काम करता है, लेकिन मैं कैसे दृश्य बटन अटक के साथ कार्यात्मक बना सकते हैं। दूसरे शब्दों में, मैं सिर्फ UIButton और अन्य UIButtons को काम करना चाहता हूं जो चीज पृष्ठभूमि में काम करती है।

मैं performSelectorInMainThread के बारे में सुना है लेकिन मैं नहीं कर सकते यह अभ्यास करने के लिए सही ढंग से

किसी भी मदद की सराहना की है :)

उत्तर

36

आप थ्रेडिंग मॉडल को बहुत समझ नहीं पाते हैं और यदि आप वास्तव में समझने के बिना एसिंक्रोनस कोड जोड़ना शुरू करते हैं तो आप पैर में खुद को शूट करने जा रहे हैं।

आपके द्वारा लिखे गए कोड मुख्य अनुप्रयोग धागे में चलते हैं। लेकिन जब आप इसके बारे में सोचते हैं, तो आपको main फ़ंक्शन नहीं लिखना पड़ता है - आप केवल एप्लिकेशन प्रतिनिधि और ईवेंट कॉलबैक (जैसे टच हैंडलर) को लागू करते हैं और किसी भी तरह से समय आने पर वे स्वचालित रूप से चलते हैं। यह एक जादू नहीं है, यह केवल Run Loop नामक कोको वस्तु है।

रन लूप एक ऑब्जेक्ट है जो सभी घटनाओं को प्राप्त करता है, प्रक्रिया टाइमर (NSTimer में) और आपका कोड चलाता है। इसका मतलब है कि जब आप, उदाहरण के लिए, कुछ करना है जब उपयोगकर्ता एक बटन टैप करता, कॉल पेड़ इस तरह एक सा दिखता है:

main thread running 
    main run loop 
     // fire timers 
     // receive events — aha, here we have an event, let’s call the handler 
     view::touchesBegan… 
      // use tapped some button, let’s fire the callback 
      someButton::touchUpInside 
       yourCode 

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

स्थिति को हल करने के लिए आपको किसी अन्य धागे में लंबे ऑपरेशन को चलाने के लिए है। यह बहुत कठिन नहीं है, लेकिन फिर भी आपको कुछ संभावित समस्याओं के बारे में सोचना होगा।एक और धागा में चल रहा है performSelectorInBackground बुला के रूप में के रूप में आसान हो सकता है:

[appDelegate performSelectorInBackground:@selector(GetApps) withObject:nil]; 

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

यदि आप performSelectorInBackground समाधान चुनते हैं, तो memory management in secondary threads के बारे में संबंधित प्रश्न देखें। आपको अपने स्वयं के ऑटोरेलीज पूल की आवश्यकता होगी ताकि आप ऑटोरेलेज्ड ऑब्जेक्ट्स को रिसाव न करें।


कुछ समय के बाद जवाब अपडेट कर रहा है - आजकल यह आम तौर पर ग्रांड सेंट्रल डिस्पैच का उपयोग कर पृष्ठभूमि में कोड को चलाने के लिए सबसे अच्छा है:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 
    // No explicit autorelease pool needed here. 
    // The code runs in background, not strangling 
    // the main run loop. 
    [self doSomeLongOperation]; 
    dispatch_sync(dispatch_get_main_queue(), ^{ 
     // This will be called on the main thread, so that 
     // you can update the UI, for example. 
     [self longOperationDone]; 
    }); 
}); 
+0

हाँ, मैंने इन चीजों पर पढ़ा है, अभी भी समझने के लिए बहुत कुछ है, लेकिन मुझे यह मिल जाएगा, आपने मुझे सही दिशा में रखा है। धन्यवाद। मैं टीबीएक्सएमएल घटक का उपयोग कर रहा था, इसलिए, मुझे लगता है कि यह समझने के लिए मुझे एक सरल लिखना होगा कि यह कैसे काम करता है। वैसे भी, एक और संबंधित सवाल यह था कि, यदि उपयोगकर्ता दृश्य को बंद करना चुनता है, तो क्या selectorInBackground कॉल को समाप्त करने के लिए वैसे भी है? ViewDidUnload .. पर? – Sam

+1

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

+0

अंतिम कोड स्निपेट अद्भुत है, यह वही है जो मैं ढूंढ रहा हूं। बहुत धन्यवाद! – kisileno

0

आप एक पृष्ठभूमि आपरेशन कि आपरेशन कतार में धकेल दिया जाता है का उपयोग कर सकते रख:

BGOperation *op = [[BGOperation alloc] init]; 
[[self operationQueue] addOperation:op]; 
[op release]; 

मैं विशिष्ट "कमांड" जो पृष्ठभूमि में निष्पादित हो बना लिया है:

@implementation BGOperation 

# pragma mark Memory Management 

- (BGOperation *)init 
{ 
if ((self = [super init]) != nil) 
    /* nothing */; 
return self; 
} 

- (void)dealloc 
{ 
self.jobId = nil; 
[super dealloc]; 
} 

# pragma mark - 
# pragma mark Background Operation 

- (void)main 
{ 
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 
    [appDelegate GetApps]; 
[pool release]; 
return; 
} 

@end

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

+1

नहीं यह 'उपयोग करने के लिए performSelectorInBackground' या अतुल्यकालिक' बहुत आसान है NSURLConnection' मोड? – zoul

+1

नहीं, क्योंकि तब आपको थ्रेड लाइफसाइकिल को संभालना होगा (दिए गए उदाहरण में उसे अपना स्वयं का ऑटोोरिज़ पूल बनाने की आवश्यकता नहीं थी)। Asynch NSUrlConnection ठीक है लेकिन यह मुख्य धागे पर काम करता है और इसलिए आपका यूआई स्टटर कर सकता है, खासकर अगर उनमें से कुछ एक बार में जा रहे हैं। ऑपरेशनक्यूयूज काम करने और प्रबंधित करने के लिए कहीं अधिक आसान हैं ... –

+0

एक ऑटोरेलीज पूल बनाना और निकालना कोड की दो पंक्तियां हैं, यह मेरे लिए एक बड़ा सौदा प्रतीत नहीं होता है। (बस एक बिंदु देखें।) – zoul

0

ऐसा लगता है कि आप अपने getApps विधि के अंदर NSURLConnection का उपयोग कर रहे हैं। यदि ऐसा है, तो आपको इसे एक एसिंक्रोनस कॉल में परिवर्तित करना चाहिए।

3

NSURLConnection की connectionWithRequest:delegate: विधि का उपयोग करें। यह निर्दिष्ट अनुरोध को असीमित रूप से भेजा जाएगा। प्रतिनिधि को कनेक्शन का जवाब देना चाहिए: didReceiveResponse: प्रतिक्रिया पूरी तरह से प्राप्त होने के बाद उस संदेश को भेजा जाएगा।

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