I am trying to add objects to a Core Data Database using the following code:
NSManagedObjectContext *managedObjectContext = [[NSManagedObjectContext alloc] init];
managedObjectContext.persistentStoreCoordinator = [[CoreDataController sharedCoreDataController] persistentStoreCoordinator];
Feed *feed = [NSEntityDescription insertNewObjectForEntityForName:#"Feed" inManagedObjectContext:managedObjectContext];
feed.feedID = dictionary[#"feed_id"];
feed.siteURL = dictionary[#"site_url"];
feed.title = dictionary[#"title"];
NSError *error;
if (![managedObjectContext save:&error]) {
NSLog(#"Error, couldn't save: %#", [error localizedDescription]);
}
Feed is a subclass of NSManagedObject. The persistentStoreCoordinator returned from the sharedCoreDataController (a singleton) is the persistentStoreCoordinator from a UIManagedDocument (created or opened when the app launches). As far as I can tell, the document is being created or opened successfully. I am running this code in the simulator, and I'm looking in the directory in which I am saving the Database (the apps Documents directory), but the persistentStore file is not being updated to reflect the new objects being added. Am I doing something wrong? I should also point out that the above code is being executed multiple times on a concurrent, asynchronous queue.
Any help would be much appreciated, thanks in advance.
Update: After the suggestions from Alexander and Duncan, the code above has been updated to reflect the changes. Sadly, however, I haven't noticed any difference (the new data is not appearing in the persistentStore file).
Have you called the line to save your managedObjectContext? How about this:
NSError *error;
if (![managedObjectContext save:&error]) {
NSLog(#"Error, couldn't save: %#", [error localizedDescription]);
}
Try using something like this
NSManagedObjectContext *managedObjectContext = [[NSManagedObjectContext alloc] init];
managedObjectContext.persistentStoreCoordinator = [[CoreDataController sharedCoreDataController] persistentStoreCoordinator];
for (NSDictionary *dictionary in arrayOfData) {
Feed *feed = [NSEntityDescription insertNewObjectForEntityForName:#"Feed" inManagedObjectContext:managedObjectContext];
feed.feedID = dictionary[#"feed_id"];
feed.siteURL = dictionary[#"site_url"];
feed.title = dictionary[#"title"];
}
NSError *error;
if (![managedObjectContext save:&error]) {
NSLog(#"Error, couldn't save: %#", [error localizedDescription]);
}
I would avoid creating a managedObjectContext for each object you insert.
First, using UIManagedDocument is not recommended. It is not intended to be your single app wide context. It is meant for document style applications.
Second, the NSManagedObjectContext that is exposed from the UIManagedDocument doesn't have a NSPersistentStoreCoordinator attached to it. It is a child context of a private NSManagedObjectContext that then has a NSPersistentStoreCoordinator. I suspect that if you used the debugger you may find that your NSManagedObjectContext is missing its NSPersistentStoreCoordinator.
In any event, you are using UIManagedDocument and then trying to attach another NSManagedObjectContext to the same NSPersistentStoreCoordinator. That is a bad design and you should at a minimum remove the UIManagedDocument or stop creating a new NSManagedObjectContext.
What is the point of the new context? What are you trying to solve with this code?
Related
anyone can describe me about core data?
I want to create worksheet which store day activity record and that data stored in local file.
I think core data is best to store locally.
Thanks in advance.
You should see CoreData not as a database, but as a way to manage a graph of object.
You can then store this graph in different places (transparently from the application point of view) such as memory, XML, sqlite, and I think custom binary file.
What you usually do is to write the model in a core data model.
Each object is either an instance of NSManagedObject (which you can query / work with with methods such as valueForKey:, setValueForKey: etc) or subclasses of that class. This subclasses can be autogenerated directly from Xcode, and at this point you almost forget you are working with CoreData. Every attribute is a #property, every to-many relationship is a NSSet.
You get back to the fact that you are using CoreData when you create and want to save the object. In this case you have to get the 'context' in which the object resides, and call method on it (e.g. save)
There is full of tutorial and documentation on the web about CoreData.
In my opinion the core point is.. don't think at it as a relational database. "Be more object oriented" :)
To getting started you can take a look at:
http://www.raywenderlich.com/934/core-data-tutorial-for-ios-getting-started
The for more complex stuff the apple doc is ok
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CoreData/cdProgrammingGuide.html#//apple_ref/doc/uid/TP30001200-SW1
Yes you are right, Core data is bast way to store data in iOS applications.
With new XCode all you need to do is when creating new project click check box that you will use coreData.
This will create yourPorject.xcdatamodeld file and some methods inside your AppDelegate file :
- (NSURL *)applicationDocumentsDirectory {
// The directory the application uses to store the Core Data store file. This code uses a directory named "result.PingPong" in the application's documents directory.
return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}
- (NSManagedObjectModel *)managedObjectModel {
// The managed object model for the application. It is a fatal error for the application not to be able to find and load its model.
if (_managedObjectModel != nil) {
return _managedObjectModel;
}
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:#"PingPong" withExtension:#"momd"];
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
return _managedObjectModel;
}
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
// The persistent store coordinator for the application. This implementation creates and return a coordinator, having added the store for the application to it.
if (_persistentStoreCoordinator != nil) {
return _persistentStoreCoordinator;
}
// Create the coordinator and store
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"PingPong.sqlite"];
NSError *error = nil;
NSString *failureReason = #"There was an error creating or loading the application's saved data.";
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
// Report any error we got.
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
dict[NSLocalizedDescriptionKey] = #"Failed to initialize the application's saved data";
dict[NSLocalizedFailureReasonErrorKey] = failureReason;
dict[NSUnderlyingErrorKey] = error;
error = [NSError errorWithDomain:#"YOUR_ERROR_DOMAIN" code:9999 userInfo:dict];
// Replace this with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return _persistentStoreCoordinator;
}
- (NSManagedObjectContext *)managedObjectContext {
// Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.)
if (_managedObjectContext != nil) {
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (!coordinator) {
return nil;
}
_managedObjectContext = [[NSManagedObjectContext alloc] init];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
return _managedObjectContext;
}
#pragma mark - Core Data Saving support
- (void)saveContext {
NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
if (managedObjectContext != nil) {
NSError *error = nil;
if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
}
(I case it didn't)
After that you create your entity object inside yourPorject.xcdatamodeld, by clicking on AddEntity, name it and inside with Add Attribute add all your attributes.
After that click on menu : Editor -> Create NSManagedObject subclasses. This will automatically create object for you.
All you need to do to save object into database is
AppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
NSManagedObjectContext *context = appDelegate.managedObjectContext;
YourObject * o = [NSEntityDescription insertNewObjectForEntityForName:#"YourObject" inManagedObjectContext:context];
o.attribute1 = attribute1;
o.attribute2 = attribute2;
[context save:nil];
To fetch all object you will need this :
AppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
NSManagedObjectContext *context = appDelegate.managedObjectContext;
NSError *fetchError;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"YourObject" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&fetchError];
if (fetchError != nil) {
NSLog(#"Error fetching database request. Reason: %#", fetchError);
}
I hope it will help you for start.
Marko
I have a relatively simple entity. When I create it, set its attributes, and save it, it saves successfully. I can later retrieve it and it is not nil and I get a successful save message from MagicalRecord.
When I retrieve it and try to access any attribute though the attribute is nil. The entity itself is fine but the attributes are all nil. I have checked they are all definitely set correctly before I save.
I haven't encountered this problem before. Why could it be occurring?
NB: This doesn't happen every time. Most times I call the method to create and save this entity it can later be retrieved without any issues. The problem is intermittent but possible to replicate on every run.
Code:
Entity1 *entity1 = [Entity1 MR_createEntityInContext:localContext];
[entity1 setUpEntity:myobject];
EntityChild *entityChild=[EntityChild MR_createEntityInContext:localContext];
[entityChild setUpEntityChild:entity.child withContext:localContext];
[entityChild setEntity1:entity1];
[localContext MR_saveToPersistentStoreWithCompletion:^(BOOL success, NSError *error) {
}];
Update:
If I look in the sqlite database and search for the entity it actually doesn't exist at all. So MagicalRecord tells me it saves, CoreData lets me retrieve a non-nil object (albeit with nil attributes) but no record exists in the database.
I did not understand ur code standards. As I am new to IOS Development. I Used below code for retrieving.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entityRef = [NSEntityDescription entityForName:#"Entity1" inManagedObjectContext:appDelegate.managedObjectContext];//localContext
[fetchRequest setEntity:entityRef];
NSError *error=nil;
NSArray *detailsArray = [appDelegate.managedObjectContext executeFetchRequest:fetchRequest error:&error];
if (error) {
NSLog(#"Unable to execute fetch request.");
NSLog(#"%#, %#", error, error.localizedDescription);
}
Saving the data
NSManagedObjectContext *context = [appDelegate managedObjectContext];//localContext
NSManagedObject *objectRef = [NSEntityDescription
insertNewObjectForEntityForName:#"Entity1" inManagedObjectContext:context];
[objectRef setValue:#"IOS" forKey:#"Name"];
[objectRef setValue:#"positive" forKey:#"Attitude"];
NSError *error;
if (![context save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
Hope it helps you...!
Ok, I got to the bottom of this. It wasn't a problem with the code when I did the save. It was actually a problem with some code in another class that was retrieving the data from the wrong context. When I changed the context it worked correctly.
I'm still not sure why this only happened occasionally and not every time the code was run but it's working now.
Thanks for your help anyway everyone.
I have a method which allows the user to export a .csv file of all data in my Core Data model. I'm using the wonderful CHCSV Parser after performing a fetchRequest to fetch the stored results. Once the .csv has been created, it gets attached to an email and exported from the device. This all works fine, the issues come when I delete the data.
I am using a somewhat popular technique to delete my data (by popular I mean it has been recommended on lots of Stack Overflow answers I have came researched). I simply fetch all objects and delete them one by one.
// Create fetchRequest
NGLSAppDelegate *appDelegate = (NGLSAppDelegate *)[[UIApplication sharedApplication]delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc]init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"NGLS"
inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSError *error;
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
// Delete objects
for (Model *results in fetchedObjects) {
[context deleteObject:(id)results];
NSLog(#"NGLS objects deleted");
};
My model is simple and only has two entities, so I use the same code for the other Admin entity too. This is all fine, all objects are deleted, and I can confirm this by doing another fetchRequest which returns the object count for each entity - both have a count of zero. The problem occurs when I try to save data back to either entity after the delete has been executed.
The ViewController with the above code is an "Admin" control screen, where the user can login and also export the data. So when the user logs in, here is the code to save:
// Save login details
[_managedObjectAdmin setValue:user forKey:#"userLogin"];
[_managedObjectAdmin setValue:site forKey:#"siteLocation"];
NSError *error;
[[self.managedObjectAdmin managedObjectContext] save:&error];
I then perform the fetchRequest above and export all data when the "Export" button is pressed. After that is complete, when I try to login on the same ViewController, the output is:
2014-10-27 12:43:49.653 NGLS[19471:607] <NSManagedObject: 0x7a8c2460> (entity: Admin; id: 0x7a82e3e0 <x-coredata://3C9F3807-E314-439C-8B73-3D4459F85156/Admin/p30> ; data: <fault>)
If I navigate back to another ViewController (using my navigation controller), then go back to the "Admin" control screen and try to login again, I get this output:
2014-10-27 12:44:04.530 NGLS[19471:607] *** Terminating app due to uncaught exception 'NSObjectInaccessibleException', reason: 'CoreData could not fulfill a fault for '0x7a82e3e0 <x-coredata://3C9F3807-E314-439C-8B73-3D4459F85156/Admin/p30>''
(I can post the full log if requested).
I have tried many things in an attempt to resolve this issue. Core Data is complex and I dont quite understand what I need to do to fix this. I have tried to delete the persistentStore and create it again, and the same with the managedObjectContext but nothing I have tried has worked. I implemented a resetStore method in my AppDelegate to delete and rebuild the store as follows:
- (void)resetStores {
NSError *error;
NSURL *storeURL = [[_managedObjectContext persistentStoreCoordinator] URLForPersistentStore:[[[_managedObjectContext persistentStoreCoordinator] persistentStores] lastObject]];
// lock the current context
[_managedObjectContext lock];
[_managedObjectContext reset];//to drop pending changes
//delete the store from the current managedObjectContext
if ([[_managedObjectContext persistentStoreCoordinator] removePersistentStore:[[[_managedObjectContext persistentStoreCoordinator] persistentStores] lastObject] error:&error])
{
// remove the file containing the data
[[NSFileManager defaultManager] removeItemAtURL:storeURL error:&error];
//recreate the store like in the appDelegate method
[[_managedObjectContext persistentStoreCoordinator] addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error];//recreates the persistent store
}
[_managedObjectContext unlock];
}
And also tried this method:
- (NSPersistentStoreCoordinator *)resetPersistentStore
{
NSError *error = nil;
if ([_persistentStoreCoordinator persistentStores] == nil)
return [self persistentStoreCoordinator];
_managedObjectContext = nil;
NSPersistentStore *store = [[_persistentStoreCoordinator persistentStores] lastObject];
if (![_persistentStoreCoordinator removePersistentStore:store error:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
// Delete file
if ([[NSFileManager defaultManager] fileExistsAtPath:store.URL.path]) {
if (![[NSFileManager defaultManager] removeItemAtPath:store.URL.path error:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
// Delete the reference to non-existing store
_persistentStoreCoordinator = nil;
NSPersistentStoreCoordinator *r = [self persistentStoreCoordinator];
return r;
}
But neither of these work. What I have found to work is after the export has completed, by closing and reopening the app everything works as normal again. I have tried to figure out what process occurs when closing/opening the app in regards to Core Data but I just don't know what to look for. I have researched many questions on Stack Overflow and tried all the solutions but nothing works for me. I really need some help on this, or else I'm going to have to force the user to quit the app after the export is done by a exit(0); command (because it's not getting submitted to the App Store, its for in-house employees) although I don't want that solution, surely there is a way I can just reset my database and continue to use the app without having to shut it down every time. Thanks in advance.
References:
Deleting all records from Core Data
Reset a Core Data persistent store
How do I delete all objects from my persistent store in Core Data
I have managed to solve the issue by simply refreshing the view. By calling my viewDidLoad method after the export is complete I create a new managedObject ready to use as normal, without leaving that ViewController or exiting the app altogether.
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib
// Create fetchRequest
NGLSAppDelegate *appDelegate = (NGLSAppDelegate *)[[UIApplication sharedApplication]delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Admin" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSError *error = nil;
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
// Fetch last object
Model *admin = [fetchedObjects lastObject];
// Populate login fields with last stored details
if (admin.userLogin == nil) {
//NSLog(#"Login empty");
} else {
self.usernameField.text = admin.userLogin;
self.siteLocationField.text = admin.siteLocation;
}
// Create new managed object using the Admin entity description
NSManagedObject *ManagedObjectAdmin;
ManagedObjectAdmin = [NSEntityDescription insertNewObjectForEntityForName:#"Admin"
inManagedObjectContext:context];
// Declare managed object
self.managedObjectAdmin = ManagedObjectAdmin;
// Save context
NSError *saveError = nil;
[context save:&saveError];
// ... more stuff
}
The solution was so simple all along. I will leave this question/answer up so other people can benefit from it if they find themselves in my situation. Thanks.
EDIT: Re-creating the managedObjectAdmin anywhere after the export works too, there is no specific need to call the viewDidLoad method. After the export has completed, I can simply create the new managedObject and save the context and continue using the app as normal, without having to navigate to another ViewController first or restarting the app.
i've created an app that uses Core-data. Now i want to see the data that i've stored in various entities in that app. App has been installed in iOS-simulator (7.0.3)
This did not help
ScreenShot:
And here is how I am saving the data..
EffController *newController = [NSEntityDescription insertNewObjectForEntityForName:#"EffController" inManagedObjectContext:self.managedObjectContext];
newController.name=#"fgfghjfghj";
newController.uniqueUID= #"ffghfg";
newController.parentOutputID = #0;
newController.localIP=#"***.***.***.***";
newController.localPort= #XXXX;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc]
initWithEntityName:#"EffControllerType"];
NSError *requestError = nil;
/* And execute the fetch request on the context */
NSArray *types = [self.managedObjectContext executeFetchRequest:fetchRequest error:&requestError];
for (EffControllerType *type in types) {
if ([type.name isEqualToString:#"***********"]) {
newController.type= type;
break;
}
}
NSError *error = nil;
if (![newController.managedObjectContext save:&error]) { //here is the mistake.
//It should be self.managedobjectContext
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
Edit After long discussion with #HaniIbrahim problem was solved… But still not clear about why the content was visible in app, not in sqlite file? & why context for managed object? Can anybody help to find real issue behind the topic?
After you insert, update or delete anything in CoreData you have to save context to actual apply your updates
NSError *error = nil;
[context save:&error]; // context is your managedObjectContext that you use to communicate with coreData
Edit
After you post your code I found that you are using two context self.managedObjectContext and newController.managedObjectContext ?
You can view the data by using SQLite Manager plugin for Firefox. Open firefox -> Install the plugin -> Open the plugin -> navigate to the below path using Open File option
Library/Application Support/iPhone Simulator/VERSION_OF_YOUR_SIMULATOR/Applications/YOUR_PROJECT(Usually in alphanumeric)/YOUR_DB_FILE.sqlite
That should do.
There are number of plugins and applications available similar to SQLite Manager. You may choose that as per your desire.
My app simply add some users informations (name, birthdate, thumbnail, ...) with Core Data.
I noticed that if I delete a user right after created it, my app just stop working (not a crash, xCode returns no crash log, nothing).
I'm using asynchronous nested context for saving my users informations so I guess that behavior is due to the fact that my delete statement is executing before my save statement.
But since i'm a total beginner with Core Data, i don't really know how to handle that. I don't even know if i declared nested contexts the right way.
Here's my save codes :
NSManagedObjectContext *tmpContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
tmpContext.parentContext = self.backgroundManagedObjectContext;
BSStudent *newStudent = (BSStudent *)[NSEntityDescription insertNewObjectForEntityForName:kBSStudent inManagedObjectContext:tmpContext];
newStudent.firstname = firstname;
newStudent.lastname = lastname;
newStudent.birthdate = birthdate;
newStudent.thumbnail = thumbnail;
newStudent.createdAt = [NSDate date];
[self dismissViewControllerAnimated:YES completion:nil];
[tmpContext performBlock:^{
[tmpContext save:nil];
[self.backgroundManagedObjectContext performBlock:^{
NSError *error;
if (![self.backgroundManagedObjectContext save:&error]) {
NSLog(#"%#", [error localizedDescription]);
}
[self.managedObjectContext performBlock:^{
NSError *error;
if (![self.managedObjectContext save:&error]) {
NSLog(#"%#", [error localizedDescription]);
}
}];
}];
}];
For precision, self.managedObjectContext is a NSPrivateQueueConcurrencyType and self.backgroundManagedObjectContext is a NSMainQueueConcurrencyType. And self.backgroundManagedObject is a child of self.managedObjectContext.
Here's my delete codes :
BSStudent *student = objc_getAssociatedObject(alertView, kDeleteStudentAlertAssociatedKey);
// on supprimer l'objet et on sauvegarde le contexte
[self.managedObjectContext deleteObject:student];
NSError *error;
if(![self.managedObjectContext save:&error]) {
NSLog(#"%#", [error localizedDescription]);
}
Can someone know how to handle this situation properly ?
Your delete is probably using the BSStudent created by a different context than you are deleting with. The following code will fix that.
NSManagedObjectContext * deleteContext = student.managedObjectContext;
[deleteContext deleteObject:student];
If you really want to use the other context, refetch the student using ObjectID
NSManagedObject * studentToDelete = [self.managedObjectContext objectWithID:student.objectID];
[self.managedObjectContext deleteObject:studentToDelete];
Nested contexts tips
Your contexts are probably okay, but I see a lot of people throwing around performBlock unnecessarily. With nested contexts, the QueueConcurrencyType refers to the thread it will do Core Data operations on, not the thread it was created on. So doing an operation like save on itself inside its performBlock is unnecessary and can lead to deadlocks.
When you save a child context, the parent is automatically synced with the changes. If you want to save upwards to the next higher parent automatically, I would recommend registering the parent for NSManagedObjectContextDidSaveNotification of the child saves. You can make this easier by having your AppDelegate have a factory method for creating the child contexts.
- (NSManagedObjectContext *)createChildContext
{
NSManagedObjectContext *tmpContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
tmpContext.parentContext = self.managedObjectContext;
//Register for NSManagedObjectContextDidSaveNotification
return tmpContext;
}
if you wrap your delete in a performBlock call it can't execute at the same time as the saving performBlock.
e.g.:
BSStudent *student = objc_getAssociatedObject(alertView, kDeleteStudentAlertAssociatedKey);
// on supprimer l'objet et on sauvegarde le contexte
[self.managedObjectContext performBlock:^{
[self.managedObjectContext deleteObject:student];
NSError *error;
if(![self.managedObjectContext save:&error]) {
NSLog(#"%#", [error localizedDescription]);
}
}];
This is the "preferred" way of dealing with contexts as it serializes access to the context and keeps all those operations on the contexts thread,
I assume you are getting the crash because the objectID is becoming invalid or changing before the save completes, near the top of the call stack you'll see something about "hash64" or such