22

मैं अपने आवेदन में ग्रांड सेंट्रल डिस्पैच (GCD) का उपयोग कर रहा कुछ बड़े कार्य करने करने के लिए के साथ। एप्लिकेशन डाटा स्टोरेज उद्देश्यों के लिए कोर-डेटा का उपयोग कर रहा है। यहाँ (प्रासंगिक सवाल के साथ) मेरी परिदृश्य है:ग्रांड सेंट्रल डिस्पैच (GCD) CoreData

dispatch_queue_t main_queue = dispatch_get_main_queue(); 
dispatch_queue_t request_queue = dispatch_queue_create("com.app.request", NULL); 

dispatch_async(request_queue, ^{ 
    MyNSManagedObject *mObject = [self.fetchedResultsController objectAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]]; 

    // … 
    // <heavy lifting> 
    // … 

    // … 
    // <update mObject> 
    // … 

    [self saveManagedObjectContext]; 
});  

[self saveManagedObjectContext] का एक परिणाम के रूप में, fetchResultsController प्रतिनिधि तरीकों स्वचालित रूप से कहा जाता है। नतीजतन, यूआई अद्यतन तर्क kicks।

अब मेरे सवाल है, मैं -saveManagedObjectContext के लिए main_queue उपयोग करने की आवश्यकता है है? मैं main_queue में मेरी NSManagedObject पर सभी गतिविधियों को अंजाम देना चाहिए? NSManagedObject को अपडेट करने वाले कुछ ऑपरेशन में 2-3 सेकंड लग सकते हैं। कृपया सलाह दें। धागा प्रति एक प्रबंधित ऑब्जेक्ट संदर्भ -

उत्तर

17

आप शायद जानते हैं या देखा है आप मुख्य थ्रेड पर यूआई कार्रवाई करना चाहिए। जैसा कि आप उल्लेख करते हैं कि जब आप यूआई अपडेट को सहेजते हैं तो होता है। आप मुख्य थ्रेड पर dispatch_sync के लिए एक कॉल घोंसले बनाने के इस हल कर सकते हैं।

dispatch_queue_t main_queue = dispatch_get_main_queue(); 
dispatch_queue_t request_queue = dispatch_queue_create("com.app.request", NULL); 

__block __typeof__(self) blockSelf = self; 

dispatch_async(request_queue, ^{ 
    MyNSManagedObject *mObject = [blockSelf.fetchedResultsController objectAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]]; 

    // update and heavy lifting... 

    dispatch_sync(main_queue, ^{ 
     [blockSelf saveManagedObjectContext]; 
    }); 
});  

blockSelfके उपयोग गलती से चक्र का संदर्भ बनाने से बचना है। (Practical blocks)

59

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

ऐसा करने के लिए आप एक नया संदर्भ बना सकते हैं और यह अपने मुख्य संदर्भ के रूप में ही लगातार दुकान दे:

NSManagedObjectContext *backgroundContext = [[[NSManagedObjectContext alloc] init] autorelease]; 
[backgroundContext setPersistentStoreCoordinator:[mainContext persistentStoreCoordinator]]; 

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

/* Save notification handler for the background context */ 
- (void)backgroundContextDidSave:(NSNotification *)notification { 
    /* Make sure we're on the main thread when updating the main context */ 
    if (![NSThread isMainThread]) { 
     [self performSelectorOnMainThread:@selector(backgroundContextDidSave:) 
           withObject:notification 
          waitUntilDone:NO]; 
     return; 
    } 

    /* merge in the changes to the main context */ 
    [self.managedObjectContext mergeChangesFromContextDidSaveNotification:notification]; 
} 

/* ... */ 

/* Save the background context and handle the save notification */ 
[[NSNotificationCenter defaultCenter] addObserver:self 
             selector:@selector(backgroundContextDidSave:) 
              name:NSManagedObjectContextDidSaveNotification 
              object:backgroundContext]; 

[backgroundContext save:NULL]; 

[[NSNotificationCenter defaultCenter] removeObserver:self 
               name:NSManagedObjectContextDidSaveNotification 
               object:syncContext]; 

notifcation बचाने हैंडलिंग और विलय महत्वपूर्ण है अन्यथा आपका मुख्य यूआई/संदर्भ परिवर्तन किए नहीं देख सकेंगे। विलय करके, आपका मुख्य fetchResultsController इत्यादि बदलती घटनाएं प्राप्त करेगा और आपके यूआई को अपडेट करेगा जैसा आप उम्मीद करेंगे।

ध्यान देने योग्य एक और महत्वपूर्ण बात यह है कि एनएसएमएनेज ऑब्जेक्ट उदाहरणों का उपयोग केवल उस संदर्भ में किया जा सकता है जिसे वे प्राप्त किए गए थे। यदि आपके ऑपरेशन को किसी ऑब्जेक्ट के संदर्भ की आवश्यकता है तो आपको ऑब्जेक्ट के objectID को ऑपरेशन में पास करना होगा और existingObjectWithID: का उपयोग करके नए संदर्भ से NSManagedObject उदाहरण को फिर से प्राप्त करना होगा। तो कुछ इस तरह:

/* This can only be used in operations on the main context */ 
MyNSManagedObject *objectInMainContext = 
    [self.fetchedResultsController objectAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]]; 

/* This can now be used in your background context */ 
MyNSManagedObject *objectInBackgroundContext = 
    (MyNSManagedObject *) [backgroundContext existingObjectWithID:[objectInMainContext objectID]]; 
+1

तो, आपकी बात यह है कि मेरे मामले में, fetchedResultsController का उपयोग करने के बजाय, मुझे एक नया प्रबंधित ऑब्जेक्ट संदर्भ बनाना चाहिए (बैकग्रो undManagedObjectContext), आवश्यक प्रबंधित ऑब्जेक्ट प्राप्त करें, आवश्यक संचालन करें, प्रबंधित ऑब्जेक्ट को अपडेट करें, इस प्रबंधित ऑब्जेक्ट संदर्भ को सहेजें (backgroundManagedObjectContext), और उसके बाद मुख्य प्रबंधित ऑब्जेक्ट संदर्भ में प्रतिबिंबित करने के लिए परिवर्तन मर्ज करें? इससे मेरा जीवन बहुत खराब हो जाएगा। – Mustafa

+0

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

+0

इस सुनहरे नियम के आसपास के तरीके हैं: https://github.com/adam-roth/coredata-threadsafe। – aroth

0

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

मान लिया जाये :

@property (nonatomic, strong) NSDictionary* threadsDictionary; 

यहाँ कैसे (धागा प्रति) प्रबंधित वस्तु प्राप्त करने के लिए है:

- (NSManagedObjectContext *) managedObjectContextForThread { 

// Per thread, give one back 
NSString* threadName = [NSString stringWithFormat:@"%d",[NSThread currentThread].hash]; 

NSManagedObjectContext * existingContext = [self.threadsDictionary objectForKey:threadName]; 
if (existingContext==nil){ 
    existingContext = [[NSManagedObjectContext alloc] init]; 
    [existingContext setPersistentStoreCoordinator: [self persistentStoreCoordinator]]; 
    [self.threadsDictionary setValue:existingContext forKey:threadName]; 
} 

return existingContext; 

}

अपने वैश्विक प्रबंधक की init विधि में कुछ बिंदु पर (मैं एक सिंगलटन प्रयुक्त): तो फिर सूचनाएं बचाने प्राप्त करते हैं और अन्य सभी प्रबंधित संदर्भ के लिए प्रचार करने के लिए

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(backgroundContextDidSave:)             name:NSManagedObjectContextDidSaveNotification             object:nil]; 

वस्तुओं:

- (void)backgroundContextDidSave:(NSNotification *)notification { 
    /* Make sure we're on the main thread when updating the main context */ 
    if (![NSThread isMainThread]) { 
     [self performSelectorOnMainThread:@selector(backgroundContextDidSave:) 
           withObject:notification 
          waitUntilDone:NO]; 
     return; 
    } 

    /* merge in the changes to the main context */ 
    for (NSManagedObjectContext* context in [self.threadsDictionary allValues]){ 
      [context mergeChangesFromContextDidSaveNotification:notification]; 
    } 
} 

(कुछ अन्य विधियों को स्पष्टता के लिए हटा दिया गया था)

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