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!
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.
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.
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.
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 NSManagedObject
s' 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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With