2011-02-27 14 views
5

के साथ सिंगलटन सिंक्रनाइज़ेड सरणी का उपयोग करना मेरे पास एक यूआईएसearchBar के साथ एक पुस्तक ऐप है, जहां उपयोगकर्ता किसी भी पुस्तक का नाम टाइप करता है और जैसा कि वह टाइप करता है, नीचे खोज परिणाम (ext API कॉल से) प्राप्त करता है।एनएसटीएचएड

मैं अपने ऐप में एक सिंगलटन वैरिएबल का उपयोग कर रहा हूं जिसे पुनर्प्राप्त किया गया है, जो सभी पुस्तकों को स्टोर करता है।

@interface Shared : NSObject { 
    NSMutableArray *books; 
} 

@property (nonatomic, retain) NSMutableArray *books; 

+ (id)sharedManager; 

@end 

यह एनएसएमयूटेबलएरे * पुनर्प्राप्त आरे का उपयोग कर एकाधिक .m फ़ाइलों में उपयोग किया जाता है; ... हेडर फाइल

retrievedArray = [[Shared sharedManager] books]; 

में मेरे सवाल यह है कि मैं सुनिश्चित करते हैं कि retrievedArray अंदर मान सभी वर्गों में सिंक्रनाइज़ रहते है।

दरअसल पुनर्प्राप्त किए गए मूल्यों को एनआरएक्सएमएलपार्सर (यानी बाहरी वेब सेवा एपीआई के माध्यम से) के माध्यम से जोड़ा जाता है। एक अलग XMLParser.m फ़ाइल है, जहां मैं सभी पार्सिंग करता हूं और सरणी भरता हूं। पार्सिंग एक अलग धागे पर किया जाता है।

- (void) run: (id) param { 
     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 

     NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL: [self URL]]; 
     [parser setDelegate: self]; 
    [parser parse]; 
     [parser release]; 

     NSString *tmpURLStr = [[self URL]absoluteString]; 

     NSRange range_srch_book = [tmpURLStr rangeOfString:@"v1/books"]; 

     if (range_srch_book.location != NSNotFound) 
      [delegate performSelectorOnMainThread:@selector(parseDidComplete_srch_book) withObject:nil waitUntilDone:YES]; 

     [pool release]; 
    } 


    - (void) parseXMLFile: (NSURL *) url 
    { 
     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 
     [self setURL: url]; 
     NSThread* myThread = [[NSThread alloc] initWithTarget:self 
                selector:@selector(run:) 


object: nil]; 
    [retrievedArray removeAllObjects]; 
    [myThread start]; 
    [pool release]; 
} 

यदि उपयोगकर्ता प्रकार बहुत जल्दी (यह अगर धीरे धीरे उपयोगकर्ता प्रकार ठीक काम कर रहा है) कुछ तुल्यकालन मुद्दों होने के लिए वहाँ लगता है .... तो वहाँ 2 दृश्य हैं जिसमें में एक वस्तु की सामग्री यह साझा सरणी आइटम प्रदर्शित होता है; सूची और विवरण। यदि उपयोगकर्ता ए में सूची दृश्य पर तेज़ और क्लिक करता है, तो उसे बी को विस्तार से देखा गया है ... यह मुख्य मुद्दा है।

मैंने सचमुच उन सभी समाधानों का प्रयास किया है जिनके बारे में मैं सोच सकता हूं, लेकिन अभी भी इस मुद्दे को ठीक करने में असमर्थ हूं।

सिंक इश्यू उदाहरण के लिए संपादन: सूची दृश्य में, यदि आइटम दिखाए गए हैं, तो आइटम 1, आइटम 2 और आइटम 3 कहें और यदि उपयोगकर्ता आइटम 2 पर क्लिक करता है, तो उसे आइटम 3 को विस्तार से देखा गया है (यानी सही नहीं कहने के लिए विवरण)

नीचे वह कोड है जो निष्पादित होता है जब सूची दृश्य में कोई आइटम क्लिक किया जाता है;

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 
    // Navigation logic -- create and push a new view controller 

    if(bookdetailCustom == nil) 
     bookdetailCustom = [[BookDetailCustom alloc] initWithNibName:@"BookDetailCustom" bundle:[NSBundle mainBundle]]; 

    //aBook = [retrievedArray objectAtIndex:indexPath.row]; 

    bookdetailCustom.selectedIndex = indexPath.row; 

    [self.navigationController pushViewController:bookdetailCustom animated:YES]; 
    [bookdetailCustom release]; 
    bookdetailCustom = nil; 
} 

यहाँ कैसे searchTabkleView

- (void) searchTableView { 
    NSString *searchText = searchBar.text; 
    NSMutableArray *searchArray = [[NSMutableArray alloc] init]; 

    for (int i=0;i<[retrievedArray count];i++) 
    { 
     Stock *aBookTemp = [retrievedArray objectAtIndex:i]; 
     NSString *temp = [aBookTemp valueForKey:@"BookName"]; 
     [searchArray addObject:temp]; 
    } 

    for (NSString *sTemp in searchArray) 
    { 
     NSRange titleResultsRange = [sTemp rangeOfString:searchText options:NSCaseInsensitiveSearch]; 

     if (titleResultsRange.length > 0) 
      [copyListOfItems addObject:sTemp]; 
    } 

    [searchArray release]; 
    searchArray = nil; 
} 

कृपया सुझाव है कि कुछ उपयुक्त सुधारों की तरह लग रहा है।

उत्तर

5

जो आपने पोस्ट किया है, उससे प्रत्येक पुनर्प्राप्त ऐरे एक ही एनएसएमयूटेबलएरे ऑब्जेक्ट पर इंगित कर रहा है।तो सिंक्रनाइज़ करने के लिए कोई अलग सरणी नहीं है, यह सब एक ही सरणी है।

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

मुझे लगता है कि घटनाओं के अनुक्रम कुछ इस तरह है:

  1. सूची का मानना ​​है कि सूचकांक एन
  2. उपयोगकर्ता प्रकार कुछ पर एक भी शामिल है परिणामों के एक सेट प्रदर्शित कर रहा है। एक्सएमएल पार्सर साझा सरणी को क्रमशः अद्यतन करना शुरू कर देता है। सूची दृश्य अभी तक अपडेट नहीं किया गया है।
  3. उपयोगकर्ता सूची दृश्य में अनुक्रमणिका एन पर आइटम को छूता है। सूची दृश्य इंडेक्स एन
  4. पर आइटम को प्रदर्शित करने के लिए विस्तार दृश्य को निर्देशित करता है। विस्तृत दृश्य साझा सरणी से इंडेक्स एन पर आइटम निकालता है, लेकिन चरण 2 इंडेक्स में शुरू किए गए अद्यतन के कारण अब बी शामिल है। प्रदर्शन देखें।
  5. किसी बिंदु पर एक्सएमएल पार्स पूरा हो जाता है, और अब सूची अपडेट की जाती है।

यह भी संभव होना चाहिए, यदि वेब सेवा से लोड और पार्स पर्याप्त धीमा हो, तो चरण 4 के लिए बस एनएसआरेंजएक्सप्शन के साथ क्रैश करने के लिए।

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

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

+0

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

+0

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

+0

आपके NSXMLParserDelegate विधियों में आपको पुनर्प्राप्ति ऑर्रे में ऑब्जेक्ट्स जोड़ना होगा। इसके बजाय, पुनर्प्राप्त किए गए एररे फ़ील्ड के साथ एक 'अस्थायीअरे' फ़ील्ड जोड़ें, इसे '[पार्सर पार्स]' पर कॉल करने से पहले एक नए एनएसएमयूटेबलएरे में सेट करें, परिणामों को अस्थायी में जोड़ें, प्रतिनिधि विधियों में अरेय करें, और उसके बाद '[पार्सर पार्स]' रिटर्न कॉल '[पुनर्प्राप्त किया गया ऐरे बदलें ऑब्जेक्ट्स इनरेंज: एनएसएमकेरेंज (0, पुनर्प्राप्त आरायराउंट) ऑब्जेक्ट्स फ्रॉमएरे: अस्थायीअरे]। – Anomie

2

मूल रूप से सुझाव दिया गया है कि आप अपनी संपत्ति घोषणा से nonatomic कीवर्ड हटा दें। परमाणु डिफ़ॉल्ट है (atomic सेटिंग नहीं है, nonatomic को छोड़कर पर्याप्त है) - जो @synchronize ब्लॉक में संश्लेषित सेटटर को लपेटकर आपके लिए थ्रेड सुरक्षा को संभालेगा।

दुर्भाग्य से, कई लोगों ने केवल nonatomic को अपने कोड पर वास्तव में समझने के बिना ही सीखा है। मैंने हमेशा सोचा है कि यह ऐप्पल नमूना कोड से प्रतिलिपि/पेस्ट से आता है - वे अक्सर यूआई से संबंधित सामानों के लिए इसका उपयोग करते हैं - याद रखें कि UIKit थ्रेड सुरक्षित नहीं है।

एनोमी ने अपने जवाब में संकेत दिया है कि यह ऐसा नहीं है - संभवतः - क्योंकि आप विभिन्न धागे से एक परिवर्तनीय सरणी को बदल रहे हैं। कि मेरे लिए सही उत्तर की तरह लगता है - मैं अपना जवाब हटा दूंगा लेकिन मैं इसे यहां छोड़ दूंगा क्योंकि मुझे लगता है कि मेरी टिप्पणियां कुछ मूल्यवान हैं (फिर भी आपकी समस्या के लिए 100% प्रासंगिक नहीं हैं)।

+0

आपके उत्तर के लिए धन्यवाद ... मैंने साझा.h फ़ाइल @property (retain) से nonatomic हटा दिया NSMutableArray * किताबें; लेकिन फिर भी समस्या होती है ... – testndtv

+1

परमाणु गेटर और सेटर होने का मतलब है कि सरणी प्राप्त करना और सेट करना परमाणु होगा, लेकिन सरणी की सामग्री तक पहुंच नहीं होगी। आप सही हैं कि यह परमाणु होना चाहिए, लेकिन उसे उस कोड के चारों ओर सिंक्रनाइज़ करने की आवश्यकता है जो सरणी को संशोधित/पढ़ता है। अद्यतन के दौरान एक अलग सरणी का उपयोग करने के @ एनोमी के समाधान का उपयोग करने के लिए यह आसान और अधिक कुशल होगा, जिस स्थिति में परमाणु सेटर्स पर्याप्त होंगे। – ughoavgfhw

+0

यदि साझा ऑब्जेक्ट में पुस्तकें सरणी में हेरफेर करने के लिए @ सिंक्रनाइज़ किए गए विधियां थीं, तो सरणी की सामग्री तक समवर्ती पहुंच हल हो जाएगी। –

0

सरणी के लिए एक्सेसर्स में NSRecursiveLock का उपयोग करने का प्रयास करें।

NSRecursiveLock दस्तावेज़ीकरण देखें। सिंहावलोकन से:

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

कोरविडियो नमूना कोड में इसके उचित उपयोग के उदाहरण हैं।

+0

मैंने पहले कभी NSRecursiveLock को सुना या उपयोग नहीं किया है। क्या आप कृपया मेरे ऐप में इसे कार्यान्वित करने के बारे में विस्तार से बता सकते हैं। – testndtv

+0

क्यों रिकर्सिव लॉक? सामान्य लॉक पर्याप्त नहीं होना चाहिए? – ughoavgfhw

0

समस्या यह है कि retrievedArray को दो धागे से संदर्भित किया जा रहा है। अपने XML पार्सिंग कोड से retrievedArray पर सभी संदर्भों को हटाएं और केवल इसे मुख्य थ्रेड पर बदलें।parsedArray = [NSMutableArray array]

  • बदलें parser:didEndElement: इस नई सरणी में जोड़ने के लिए: एक नई सरणी बनाने के लिए

    1. बदलें parseXMLFile::

      यहाँ प्रक्रिया है [parsedArray addObject:aBook]

    2. parser:didEndDocument: मुख्य लिए रवाना अपने नए सरणी पारित धागा:

      [delegate performSelectorOnMainThread: @selector(updateRetrievedArray:) 
                withObject: parsedArray 
               waitUntilDone: NO]; 
      
    3. - (void) updateRetrievedArray: (NSArray *)parsedArray { 
          [retrievedArray setArray:parsedArray]; 
          [self parseDidComplete_srch_book]; // Be sure to call [tableView reloadData] 
      } 
      
  • +0

    पुनर्प्राप्त ऐरे एक साझा सरणी है और XMLParser में निम्नानुसार अपडेट किया गया है। हर बार पार्सिंग किए जाने पर पहले सभी ऑब्जेक्ट हटा दिए जाते हैं; - (शून्य) parseXMLFile: (NSURL *) यूआरएल {\t \t [पुनर्प्राप्त निकालें निकालेंऑल ऑब्जेक्ट्स]; .... } फिर पार्सर में: didEndElement ( च ([elementName isEqualToString: @ "BookDetails"]) \t \t [retrievedArray addObject: abook]; } मुझे देकर हमें बताएं अगर आपको कोई अतिरिक्त विवरण चाहिए तो – testndtv

    +0

    मैंने आपकी टिप्पणी के आधार पर अपना उत्तर अपडेट किया। इसे आज़माएं और मुझे बताएं कि क्या यह आपकी समस्या का समाधान नहीं करता है। – skue

    +0

    हाँ यकीन है..मैं इसे अपने ऐप में कोशिश और कार्यान्वित करूँगा मैं निष्कर्षों के साथ वापस आऊंगा। – testndtv

    1

    मैं समझता हूँ कि आप में समय और प्रयास की एक बहुत निवेश किया है: इस तरह से केवल एक धागा इस वस्तु परिवर्तन - 10 updateRetrievedArray: मुख्य थ्रेड पर चल रहा कोड retrievedArray को अद्यतन करने के लिए जिम्मेदार होगा इसे ठीक करना और इसके लिए एनोमी का समाधान इष्टतम है। लेकिन शायद एक अलग तरह का दृष्टिकोण लागू करना आसान हो सकता है।

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

    यह कोशिश करने लायक है और मुझे आशा है कि इससे मदद मिलती है।