Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS Core Data when to save context?

Tags:

I'm having random crashes with core data due to concurrency and multithreading. I know that core data is not thread safe. I also found a couple other answers on how to create a ThreadedDataService and instantiate separate context for each thread.

This is a bit too much for me to swallow at the moment, so I'm trying to find an easier way out.

The solution that I'm trying at the moment is simple: saving data through the main thread. However, now a new issue arises: deadlock. The app becomes unresponsive because each of my insertions of a new NSManagedObject is followed by a call to save. (that's my best guess).

Reading the App Delegate documentation, I noticed that it advises me to save context in applicationWillTerminate.

My question is this: For a long running operation that inserts new events every minute, and the user is not required to see updates propagated to all controllers immediately, when is it a good time for me to save the context? I'm getting a feeling that saving context for each record may be an overkill?

-(void)insertNewEvent {       // Create a new instance of the entity managed by the fetched results controller.     NSManagedObjectContext *context = [self.fetchedResultsController.managedObjectContext];     NSEntityDescription *entity = [[self.fetchedResultsControllerfetchRequest] entity];     Event*newManagedObject = (Event*)[NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:context];  //use accessor methods to set default values      // Save the context. > IS THIS REALLY NEEDED HERE?     NSError *error = nil;     if (![context save:&error])     {       }else     {         if(newManagedObject!=nil)         {             currentState= newManagedObject;             [currentRecord addEvent:newManagedObject]; //Is this call needed?             [self saveApplicationRecords];               }     }  } 

I have methods like these defined for all of my managed objects, is it enough if I call such method on a main thread every 10-15 minutes to save pending changes, rather than doing so after each record insertion?

-(void)saveApplicationRecords {      NSLog(@"saveApplicationRecords");     NSManagedObjectContext *context = [self.applicationRecordsController.managedObjectContext];     // Save the context.     NSError *error = nil;     if (![context save:&error])     {         NSLog(@"Unresolved error %@, %@", error, [error userInfo]);      }  } 

An extra question after reading macbirdie's response: is this kind of method legal in core data?

-(Event*)insertAndReturnNewEventWithDate:(NSDate*)date_ type:(int)type {  NSManagedObjectContext *context = [self.dreamEventsController managedObjectContext];     NSEntityDescription *entity = [[self.dreamEventsController fetchRequest] entity];     DreamEvent *newManagedObject = (Event*)[NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:context];  //handle properties   NSError *error = nil;     if (![context save:&error])     { return nil;      }else     { return newManagedObject ; }  } 

Thank you!

like image 314
Alex Stone Avatar asked Dec 10 '11 16:12

Alex Stone


People also ask

What is context in Core Data iOS?

From your perspective, the context is the central object in the Core Data stack. It's the object you use to create and fetch managed objects, and to manage undo and redo operations. Within a given context, there is at most one managed object to represent any given record in a persistent store.

Do I need Core Data?

The next time you need to store data, you should have a better idea of your options. Core Data is unnecessary for random pieces of unrelated data, but it's a perfect fit for a large, relational data set. The defaults system is ideal for small, random pieces of unrelated data, such as settings or the user's preferences.

What is iOS context?

A context consists of a group of related model objects that represent an internally consistent view of one or more persistent stores. Changes to managed objects remain in memory in the associated context until Core Data saves that context to one or more persistent stores.


1 Answers

You don't have to save the context that early in the process, especially when you want to modify the object afterwards.

In most cases you should create a separate NSManagedObjectContext for the changes you're about to perform on the database. So create objects on it, fill out the properties needed, then send save and perform the whole mergeChangesFromContextDidSaveNotification: trick with the main context (most likely running on the main thread, so using performSelectorOnMainThread... message).

By default an object created and returned by NSManagedObjectContext is autoreleased. If you've created a new object and want to edit it in a form sheet for example, you can call setRetainsRegisteredObjects: with YES to the managed object context before creating the object, so it holds on to the object created until you're done with it. Remember that it's not recommended to manage the NSManagedObjects' lifecycle on your own - you should let the NSManagedObjectContext do it. So, having that in mind, you don't have to retain the NSManagedObject. After the save operation it's unregistered by the context and removed from memory. Nothing is required on your side.

Answer to updated question part

It would be probably better if you returned an NSManagedObjectID (using [object objectID]) instead of the object itself. It allows to safely dispose of the object by the context and if one needs the object for further editing or data retrieval (e.g. from other contexts), they can fetch it from the store separately.

Even if you don't save the context, the newly created object is there and then you can decide if you want to keep the object or not.

After saving on the other hand, if the context still exists, it may return the object with given NSManagedObjectID to you from memory cache - without touching the database.

On yet another hand ;), in your case, you can pretty much safely return the object, since the NSManagedObjectContext creating it still exists, so the object will still exist as well, although already in the autorelease pool.

like image 65
macbirdie Avatar answered Oct 22 '22 00:10

macbirdie