2012-11-20 22 views
6

मैं Core Data with a Single Shared UIManagedDocument पर जस्टिन ड्रिस्कॉल के कार्यान्वयन का उपयोग कर रहा हूं। जब तक मैं इसे आईपैड स्टोरीबोर्ड पर नहीं ले जाता और आईपैड ऐप के लिए स्प्लिटव्यू कंट्रोलर तक नहीं जाता तब तक सब कुछ मेरे आईफोन ऐप में ठीक था। समस्या openwithCompletionHandler को दो बार बुलाया जा रहा है, एक बार मेरे मास्टर व्यू सेडिडलोड में और फिर मेरे विवरण में ViewWillLoad देखें। कॉल त्वरित उत्तराधिकार में हैं और चूंकि दस्तावेज़ अभी भी UIDocumentStateClosed में है, जब सिंगलटन के मेरे प्रदर्शन WithDocument विधि (नीचे) में दूसरा कॉल किया जाता है, तो ऐप क्रैश हो जाता है। मैंने iOS5.1: synchronising tasks (wait for a completion) पोस्ट के लिए e_x_p का जवाब देखा लेकिन @Sychronized इस मामले में काम नहीं करेगा क्योंकि नीचे दिए गए प्रदर्शन के साथ ही उसी थ्रेड पर कॉल किया गया है। मैं OpenwithCompletionHandler के साथ खोलने के लिए एकाधिक कॉल के खिलाफ कैसे रक्षा करूं? इसके खिलाफ सुरक्षा के लिए एकमात्र तरीका यह है कि ऊपर दिए गए कॉल में से किसी एक के निष्पादन को रोकना है जब तक कि मुझे यकीन नहीं है कि UIDocumentStateNormal सत्य है और फिर रिलीज़ करें। हालांकि वह मुख्य यूआई थ्रेड को फ्रीज करेगा जो अच्छा नहीं है। यूआई को ठंडा किए बिना इसे करने का सबसे अच्छा तरीका क्या होगा?UIManagedDocument सिंगलटन कोड openWithCompletionHandler को दो बार और क्रैश कहा जाता है

UIManagedDocumentSingleton कोड से:

- (void)performWithDocument:(OnDocumentReady)onDocumentReady 
{ 
    void (^OnDocumentDidLoad)(BOOL) = ^(BOOL success) 
    { 
     onDocumentReady(self.document); 
    }; 

    if (![[NSFileManager defaultManager] fileExistsAtPath:[self.document.fileURL path]]) 
    { 
     //This should never happen******************* 
     [self.document saveToURL:self.document.fileURL 
       forSaveOperation:UIDocumentSaveForCreating 
       completionHandler:OnDocumentDidLoad]; 

    } else if (self.document.documentState == UIDocumentStateClosed) { 
     [self.document openWithCompletionHandler:OnDocumentDidLoad]; 
    } else if (self.document.documentState == UIDocumentStateNormal) { 
     OnDocumentDidLoad(YES); 
    } 
} 

उत्तर

0

कि numberOfRowsInSection: और cellForRowAtIndexPath: बीच साझा किया जाता कोड के ब्लॉक केवल एक बार बुलाया जाना चाहिए। numberOfRowsInSection हमेशा कोशिकाओं प्रस्तुत करना tableView की कोशिश करता से पहले बुलाया जाएगा, ताकि आप जब अपनी कोशिकाओं प्रतिपादन एक NSArray उद्देश्य यह है कि आप में लाने का अनुरोध के परिणामों स्टोर कर सकते हैं इस सरणी बनाने चाहिए, और उसके बाद का उपयोग करें:

@implementation FooTableViewController { 
    NSArray *_privateArray; 
} 

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 
{ 
    [[UIManagedDocumentSingletonHandler sharedDocumentHandler] performWithDocument:^(FCUIManagedDocumentObject *document) { 
     NSManagedObjectContext * context = document.managedObjectContext; 

     NSFetchRequest * request = [NSFetchRequest fetchRequestWithEntityName:@"FCObject"]; 
     NSPredicate * searchStringPredicate = nil; 
     if (searchFilterString) 
     { 
      searchStringPredicate = [NSPredicate predicateWithFormat:@"word BEGINSWITH[c] %@",searchFilterString]; 
     } 
     request.predicate = searchStringPredicate; 
     request.shouldRefreshRefetchedObjects = YES; 
     NSError * error; 
     _privateArray = [context executeFetchRequest:request error:&error]; 
     }]; 
    return _privateArray.count; 
} 

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    static NSString *CellIdentifier = @"FCCell"; 
    FCardCell *cell = (FCCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 

    // Configure the cell... 
    FCManagedObject * fcc = [_privateArray objectAtIndex:indexPath.row]; 
    cell.isWordVisible.on = fcc.isUsed; 
    cell.fWord.text = fcc.word; 
    return cell; 
} 

मुझे ब्लॉक के भीतर सेट करने के लिए NSArray के साथ कुछ विशेष करने की आवश्यकता है, तो मुझे अपने सिर के शीर्ष से निश्चित नहीं है (एक ला __block)।

इसका मुख्य कारण यह है कि आपको यह सुनिश्चित करने की आवश्यकता है कि पंक्तियों का # निर्धारित करने के लिए डेटासेट का 100% वही आकार जैसा ही आप अपनी कोशिकाएं बना रहे हों। यदि वे मेल नहीं खाते तो आप दुर्घटनाग्रस्त हो जाएंगे। चूंकि आपके पास कोई ब्लॉक नहीं है, इसलिए आपको अब UITableViewCell अपडेट बनाने के लिए प्रेषण करने की आवश्यकता नहीं है।

अंत में अगर UIDocumentStateClosed कारण समस्याएं हो रही आप या तो उन्हें अपने NSFetch परिणामों से बाहर फ़िल्टर करना चाहिए (अतिरिक्त विधेय, NSCompoundPredicate देखने के यदि आवश्यक हो) या में cellForRowAtIndexPath:

+0

मैं एक ही प्रारंभिक कॉल जो cellForRowAtIndexPath और numberOfRowsInSection अब इस प्रकार का उपयोग करने के लाने का अनुरोध समेकित है अब और समस्या नहीं है, लेकिन समस्या अभी भी मौजूद। मैंने इसे सरल बनाने के लिए इस मुद्दे को अद्यतन किया है। –

+0

क्या आप ऑब्जेक्ट पर सिंक करने के लिए 'सिंक्रनाइज़ (दस्तावेज़ ऑब्जेक्ट)' कर सकते हैं? छोड़कर यह एक तरीका है कि आप मास्टर/विस्तार से प्राधिकारी को प्रतिनिधि दे सकते हैं ताकि उनमें से केवल एक को कॉल करने की आवश्यकता हो (या यदि उनके पास निष्पादन आदेश की गारंटी है, तो आप उनके बीच एक संदेश (संभवतः 'प्रोटोकॉल' के माध्यम से) पास कर सकते हैं पहली बार किया जाता है जब दूसरे पर निष्पादित करें। –

1

यह दिलचस्प है और निश्चित रूप से एक दोष में उन्हें बेहतर संभाल करने कोड है मेरी कोड (क्षमा करें!)। मेरा पहला विचार आपके दस्तावेज़ हैंडलर क्लास में एक संपत्ति के रूप में एक धारावाहिक कतार जोड़ना होगा और उस पर जांच करना होगा।

self.queue = dispatch_queue_create("com.myapp.DocumentQueue", NULL); 

और फिर performWithDocument में:

dispatch_async(self.queue, ^{ 
    if (![[NSFileManager defaultManager] fileExistsAtPath... // and so on 
}); 

लेकिन है कि या तो ...

आप एक BOOL झंडा सेट कर सकते हैं जब आप saveToURL कॉल और कॉलबैक में यह स्पष्ट कार्य नहीं करेगा। फिर आप उस ध्वज की जांच कर सकते हैं और फ़ाइल का निर्माण होने पर थोड़ी देर बाद performWithDocument को कॉल करने के लिए performSelectorAfterDelay का उपयोग कर सकते हैं।

2

मैंने किया के रूप में जस्टिन सुझाव नीचे ऊपर। ~ 20k उपयोगकर्ताओं के साथ दो साल के लिए मेरी क्षुधा में से एक में ठीक काम करता है।

@interface SharedUIManagedDocument() 
@property (nonatomic)BOOL preparingDocument; 
@end 

- (void)performWithDocument:(OnDocumentReady)onDocumentReady 
{ 
    void (^OnDocumentDidLoad)(BOOL) = ^(BOOL success) { 
     onDocumentReady(self.document); 
     self.preparingDocument = NO; // release in completion handler 
    }; 

    if(!self.preparingDocument) { 
     self.preparingDocument = YES; // "lock", so no one else enter here 
     if(![[NSFileManager defaultManager] fileExistsAtPath:[self.document.fileURL path]]) { 
      [self.document saveToURL:self.document.fileURL forSaveOperation:UIDocumentSaveForCreating completionHandler:OnDocumentDidLoad]; 
     } else if (self.document.documentState == UIDocumentStateClosed) { 
      [self.document openWithCompletionHandler:OnDocumentDidLoad]; 
     } else if (self.document.documentState == UIDocumentStateNormal) { 
      OnDocumentDidLoad(YES); 
     } 
    } else { 
     // try until document is ready (opened or created by some other call) 
     [self performSelector:@selector(performWithDocument:) withObject:onDocumentReady afterDelay:0.5]; 
    } 
} 

स्विफ्ट (बहुत परीक्षण नहीं)

typealias OnDocumentReady = (UIManagedDocument) ->() 

class SharedManagedDocument { 

private let document: UIManagedDocument 
private var preparingDocument: Bool 

static let sharedDocument = SharedManagedDocument() 

init() { 
    let fileManager = NSFileManager.defaultManager() 
    let urls = fileManager.URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask) 
    let documentsDirectory: NSURL = urls.first as! NSURL 
    let databaseURL = documentsDirectory.URLByAppendingPathComponent(".database") 
    document = UIManagedDocument(fileURL: databaseURL) 
    let options = [NSMigratePersistentStoresAutomaticallyOption : true, NSInferMappingModelAutomaticallyOption : true] 
    document.persistentStoreOptions = options 
    preparingDocument = false 
} 

func performWithDocument(onDocumentReady: OnDocumentReady) { 

    let onDocumentDidLoad:(Bool) ->() = { 
     success in 
     onDocumentReady(self.document) 
     self.preparingDocument = false 
    } 
    if !preparingDocument { 
     preparingDocument = true 
     if !NSFileManager.defaultManager().fileExistsAtPath(document.fileURL.path!) { 
      println("Saving document for first time") 
      document.saveToURL(document.fileURL, forSaveOperation: .ForCreating, completionHandler: onDocumentDidLoad) 
     } else if document.documentState == .Closed { 
      println("Document closed, opening...") 
      document.openWithCompletionHandler(onDocumentDidLoad) 
     } else if document.documentState == .Normal { 
      println("Opening document...") 
      onDocumentDidLoad(true) 
     } else if document.documentState == .SavingError { 
      println("Document saving error") 
     } else if document.documentState == .EditingDisabled { 
      println("Document editing disabled") 
     } 
    } else { 
     // wait until document is ready (opened or created by some other call) 
     println("Delaying...") 
     delay(0.5, closure: { 
      self.performWithDocument(onDocumentReady) 
     }) 
    } 
} 

private func delay(delay:Double, closure:()->()) { 
    dispatch_after(
     dispatch_time(
      DISPATCH_TIME_NOW, 
      Int64(delay * Double(NSEC_PER_SEC)) 
     ), 
     dispatch_get_main_queue(), closure) 
} 
} 
+1

अच्छा, यह व्लादिमीर को स्पेलिंग के लिए धन्यवाद! – Corey

+0

"//" लॉक ", इसलिए कोई और यहां प्रवेश नहीं करता है" - यह एक बहुत बड़ी गलती है – adnako

+0

@adnako यदि आप भाषाविज्ञान की बात करते हैं, मैं मानता हूं कि "लॉक" यहां तक ​​कि एक अच्छा शब्द नहीं है जब तक कि यह सिर्फ एक ध्वज नहीं है। लेकिन अगर आपको लगता है कि इस दृष्टिकोण में कमजोर पक्ष हैं तो कृपया समझाएं, मुझे वास्तव में दिलचस्पी है! –