NSFetchedResultsController do not get updated when the managedobjectcontext change - core-data

I make a program where I sometimes moves some anchor to another
When I move those anchors I would recompute distance of bizs nearby the 2 anchors (before and after anchors). The computation is done in background
I used this standard code to update stuff
+(void)commit {
// get the moc for this thread
[Tools breakIfLock];
NSManagedObjectContext *moc = [self managedObjectContext];
NSThread *thread = [NSThread currentThread];
DLog(#"threadKey commit%#" , [[self class]threadKey]);
if ([thread isMainThread] == NO) {
// only observe notifications other than the main thread
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(contextDidSave:) name:NSManagedObjectContextDidSaveNotification object:moc];
}
NSError *error;
if (![moc save:&error]) {
CLog(#"Error in Saving %#", error);
DLog(#"What the hell error is it");
}
else{
}
if ([thread isMainThread] == NO) {
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSManagedObjectContextDidSaveNotification object:moc];
}
//[GrabClass StopNetworkActivityIndicatorVisible];
}
+(void)contextDidSave:(NSNotification*)saveNotification {
dispatch_async(dispatch_get_main_queue(), ^{
BadgerNewAppDelegate *delegate = [BNUtilitiesQuick appDelegate];
DLog (#"currentThreadinContextDidSave: %#",[self threadKey]);
NSManagedObjectContext *moc = delegate.managedObjectContext; //delegate for main object
CLog(#"saveNotification : %#",saveNotification);
[moc mergeChangesFromContextDidSaveNotification:saveNotification];
});
//[moc performSelectorOnMainThread:#selector(mergeChangesFromContextDidSaveNotification:) withObject:saveNotification waitUntilDone:YES];
}
I break point and see that distances did get updated. Everything is fine
However the NSFetchedResultsController fetchedObjects doesn't seem to get updated and still use the old value.
How can that be?
Also the
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
DLog(#"controllerWillChangeContent: %#", controller);
[self.tableViewA beginUpdates];
}
is never called even though the NSManagedObjectContext has changes.
Well actually I wasn't sure if the managedObjectContext has changed or not. How do I know? I mean will change in managedObjectContext ensure changes in fetchController.fetchedObjects.
There is no caching as far as I know. How can I be sure of that too?

The NSFetchedResultsController documentation for fetchedObjects property states:
The results array only includes instances of the entity specified by
the fetch request (fetchRequest) and that match its predicate. (If the
fetch request has no predicate, then the results array includes all
instances of the entity specified by the fetch request.)
The results array reflects the in-memory state of managed objects in
the controller’s managed object context, not their state in the
persistent store. The returned array does not, however, update as
managed objects are inserted, modified, or deleted.
Availability Available in iOS 3.0 and later.
I can't say what the appropriate workaround is. My first thought is to call performFetch: in controllerDidChangeContent: in the delegate implementation.
The fetchedObjects array appears to update simply by overriding controllerDidChangeContent: with an empty implementation. This is the case using both the iPad and the iPad simulator for iOS 5.1.
There's clearly some discrepancy between the documentation and what I have observed. I have no explanation. Sorry. I can only suggest that you perform the fetch in controllerDidChangeContent: just to be safe.

Related

A core data model object deleted in one MOC can still be used in another

This is a test on part of Apple's Core Data PG, which I quote here
You started with a strong reference to a managed object from another object in your application.
You deleted the managed object through the managed object context.
You saved changes on the object context.
At this point, the deleted object has been turned into a fault. It isn’t destroyed because doing so would violate the rules of memory management.
Core Data will try to realize the faulted managed object but will fail to do so because the object has been deleted from the store. That is, there is no longer an object with the same global ID in the store.
So I setup a test project to see if it is the real case.
I'm using MagicalRecord to save some troubles creating MOCs, the code is based on a Core data model Class named "People"
#interface People : NSManagedObject
#property (nonatomic) int64_t userID;
#property (nullable, nonatomic, retain) NSString *name;
#end
In the test part, I wrap the MOCs MagicalRecord created into backgroundMOC and UIMOC so that those who are not familiar with MagicalRecord won't be confused.
UIMOC is BackgroundMOC's child and will merge backgroundMOC's changes by listening to NSManagedObjectContextDidSaveNotification backgroundMOC send out.
The "saveWithBlockAndWait" is just a wrapper around "performBlockAndWait". So here comes,
[[self backgroundMOC] MR_saveWithBlockAndWait:^(NSManagedObjectContext * _Nonnull localContext) {
People *people = [People MR_createEntityInContext:localContext];
people.userID = 1;
people.name = #"Joey";
}];
People *peopleInMainThread = [People MR_findFirstInContext:[self UIMOC]];
NSLog(#"Before delete, name = %#", peopleInMainThread.name);
[[self backgroundMOC] MR_saveWithBlockAndWait:^(NSManagedObjectContext * _Nonnull localContext) {
People *people = [People MR_findFirstInContext:localContext];
NSLog(#"Deleting, name = %#", people.name);
[localContext deleteObject:people];
}];
NSLog(#"After delete, name = %#", peopleInMainThread.name);
[[self UIMOC] save:nil];
NSLog(#"After save UIMOC, name = %#", peopleInMainThread.name);
The NSLog result is
Before delete, name = Joey //As expected
Deleting, name = Joey //As expected
After delete, name = Joey //Shouldn't it be nil already?
After save UIMOC, name = null //As expected
This result seems to state that Merge from parent MOC won't make model objects fault, which could lead to some hard-to-find bugs or instead tedious checking codes everywhere.
Again with the people object. I'll have to do things like this
- (void)codesInSeriousApp
{
[[self backgroundMOC] MR_saveWithBlockAndWait:^(NSManagedObjectContext * _Nonnull localContext) {
People *people = [People MR_createEntityInContext:localContext];
people.userID = 1;
people.name = #"Joey";
}];
__block People *people = nil;
[[self UIMOC] performBlockAndWait:^{
people = [People MR_findFirstInContext:[self UIMOC]];
}];
[self sendHttpRequestViaAFNetworking:^{
//this block is executed on main thread, which is AFNetworking's default behavior
if ([[self UIMOC] existingObjectWithID:people.objectID error:NULL])//[people isFault] would be NO here, and people's properties stay still.
{
//do something
}
else
{
//the people object is gone
//maybe some codes on another thread deleted it and save to the backgroundMOC
//the UIMOC merge the changes sent by notification, but the people object is still NOT FAULT!
}
}];
}
As far as I can tell, for any model non-fault object in a specific MOC, say MOCA, the object won't be fault until [MOC save:&error] called all the way down to the persistent store.
What really confuse me is, if Another MOC, already know that the object is fault by doing the saving chain, and MOCA merged changes that very MOC send out, how come the object in it is still non-fault?
Am I misunderstood or anything? Any reply would be appreciated.
Thx in advance :)

Importing with MagicalRecord + AFNetworking

I'm using AFNetworking and MagicalRecord (the current develop branch) and I'm trying to figure out how to import a lot of objects which are dependent on each other. Each resource/entity has multiple pages worth of downloads. I have a class managing the downloads for a given entity and saving them using MagicalDataImport (which has been amazing).
I believe my issue is that the imports aren't happening on the same thread. So I think what is happening is:
In one thread, EntityA is getting saved properly and propagated to the parent entity.
Then in another thread, EntityB is being saved, and along with it it's relationship to EntityA is built. That means a blank (fault?) object is being created. Then when it gets propagated to the parent entity, I believe EntityA is overwriting the EntityA that is there. Thus I'm left with some objects that don't have all of the attributes.
At least, I think that is what is happening. What I'm seeing via the UI is actually that the relationships between entities aren't always built correctly.
My end goal is to get the entire download/import process to be done in the background, not effecting the UI at all.
Here is my AFJSONRequest:
AFJSONRequestOperation *operation = [AFJSONRequestOperation
JSONRequestOperationWithRequest:request
success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON)
{
[self saveResources:[JSON objectForKey:#"data"]];
}
failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON)
{
DLog(#"%#",error.userInfo);
[self.webService command:self didFail:error.localizedDescription];
}];
[operation setQueuePriority:self.priority];
And it calls saveResources::
- (void)saveResources:(NSArray*)resources {
BOOL stopDownloads = [self stopDownloadsBasedOnDate:resources];
if ([resources count] > 0 && !stopDownloads){
self.offset = #([offset intValue] + [resources count]);
[self send];
}
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *blockLocalContext) {
[self.classRef MR_importFromArray:resources inContext:blockLocalContext];
} completion:^(BOOL success, NSError *error) {
if (error){
// ... handle errors
}
else {
// ... handle callbacks
}
}];
}
This kicks off another download ([self send]) and then saves the objects.
I know by default AFNetworking calls the callback in the main queue, and I've tried setting the SuccessCallbackQueue/FailureCallbackQueue to my background thread, but that doesn't seem to solve all the issues, I still have some relationships going to faulted objects, though I think I do need to do that to keep everything going in a background thread.
Is there anything else I need to call in order to properly propagate these changes to the main context? Or is there a different way I need to set this up in order to make sure that all the objects are saved correctly and the relationships are properly built?
Update
I've rewritten the issue to try to give more clarification to the issues.
Update
If you need more code I created a gist with (I believe) everything.
I ended up having this exact same issue a few days ago. My issue was I had received a customer record from my API with AFNetworking. That customer could have pets, but at this point I didn't have the petTypes to correspond to the customers pet record.
What I did to resolve this was create a transformable attribute with an NSArray which would temporarly store my pets until my petTypes were imported. Upon the importation of petTypes I then triggered an NSNotificationCenter postNotification (or you can just do the pet import in the completion).
I enumerated through the temporary transformable attribute that stored my pet records and then associated the with the petType
Also I see you are doing your import inside of a save handler. This is not needed. Doing your MR_importFromArray will save automatically. If you are not using an MR_import method then you would use the saveToPersistentStore methods.
One thing is I don't see where you are associating the relationships. Is EntityB's relationship to EntityA being sent over via JSON with the EntityA objecting being in EntityB?
If so then this is where the relationship is getting messed up as it is creating / overwriting the existing EntityA for the one provided in EntityB. My recommendation would be to do something like this.
NSArray *petFactors = [responseObject valueForKeyPath:#"details.items"];
NSManagedObjectContext *currentContext = [NSManagedObjectContext MR_context];
Pets *pet = [Pets MR_findFirstByAttribute:#"id" withValue:petId inContext:currentContext];
pet.petFactors = nil;
for (id factor in petFactors) {
[pet addPetFactorsObject:[PetFactors MR_findFirstByAttribute:#"id" withValue:[factor valueForKey:#"factorId"]]];
}
[currentContext MR_saveToPersistentStoreWithCompletion:^(BOOL success, NSError *error) {
if (success) {
NSLog(#"SAVED PET FACTORS");
[[NSNotificationCenter defaultCenter] postNotificationName:kPetFactorsSavedSuccessfully object:nil];
} else {
NSLog(#"Error: %#", error);
}
}];
I'm putting this as an answer, though I'm not 100% sure if this is your issue or not. I think the issue stems from your localContext. Here is a sample web request method from an app we wrote that uses data importing, you may be able to use it as an example to get yours working.
Note that the AFNetworking performs its completion block on the main thread, then the MagicalRecord saveInBackground method switches back to a background thread to do the importing and processing, then the final MR completion block performs the handler block on the main thread again. The localContext that's used to import is created/managed by the saveInBackground method. Once that method is complete the context is saved and merged with the app's main context and all the data can then be accessed.
- (void)listWithCompletionHandler:(void (^)(BOOL success))handler{
[[MyAPIClient sharedClient] getPath:#"list.json" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject){
NSString *statusString = [responseObject objectForKey:#"status"];
// Handle an error response
if(![statusString isKindOfClass:[NSString class]] || ![statusString isEqualToString:#"success"]){
// Request failure
NSLog(#"List Request Error: %#", statusString);
NSLog(#"%#", [responseObject objectForKey:#"message"]);
if(handler)
handler(NO);
return;
}
NSArray *itemsArray = [responseObject objectForKey:#"items"];
[MagicalRecord saveInBackgroundWithBlock:^(NSManagedObjectContext *localContext){
// Load into internal database
NSArray *fetchedItems = [Item importFromArray:itemsArray inContext:localContext];
NSLog(#"Loaded %d Items", [fetchedItems count]);
} completion:^{
if(handler)
handler(YES);
}];
} failure:^(AFHTTPRequestOperation *operation, NSError *error){
NSLog(#"Fail: %#", error);
if(handler)
handler(NO);
}];
}

RestKit & Core Data - Reloading data in table view nested in a view container

I'm working on a sync process where the latest changed objects are loaded from a server with RestKit using a timestamp and saving all changes using Core Data based on the MOC idea.
Sequence
1) Starting the sync process and save changes to Core Data [AppDelegate]
2) Refetching data from Core Data after process is finished and reload data [ContactTableView]
3) Reloading input views when fetch is complete [ContactDetailView]
4) Reloading table view placed in ViewContainer inside of ContactDetailView [ContactDetailTV]
Passing data between views
ContactTableView (passing object when user selects row)
--» ContactDetailView (passing object to subView in viewContainer)
--» ContactDetailTV (displaying data of object in a tableView)
Problem
That works
When I'm inside of ContactTableView the table view is reloaded with the new fetched data and everything is fine.
That doesn't work
When I'm inside of ContactDetailTV (because the data can change when I'm looking at it) the view displays the old data inside of the ContactDetailTV even after calling [self.tableView reloadData].
Question
What am I doing wrong or what is missing to get the reloading to work like expected?
Code
AppDelegate
- (void)startSyncProcess:(NSInteger)operationCode
{
// Init helper
syncCount = 0;
syncProcess = 0;
switch (operationCode)
{
default:
// (1) Request deleted companys from server
[self requestDeletedCompanysFromServer];
break;
case 0:
// (2) Request deleted contacts from server
[self requestDeletedContactsFromServer];
break;
case 1:
// (2) Request edited companys from server
[self requestEditedCompanysFromServer];
break;
case 2:
// (2) Request edited contacts from server
[self requestEditedContactsFromServer];
break;
case 3:
// Reload data views
[self saveCurrentSyncTime];
[[NSNotificationCenter defaultCenter] postNotificationName:#"updateTableViewAfterSave" object:self];
break;
}
}
ContactTableView
- (void)syncContextDidSave:(NSNotification*)saveNotification
{
// Handling the "updateTableViewAfterSave"-notification by refetching the data with Core Data
NSError *error;
if (![[self fetchedResultsController] performFetch:&error])
NSLog(#"[ERROR] Saving failed with error: %#", [error userInfo]);
[[NSNotificationCenter defaultCenter] postNotificationName:#"updateSubViewAfterSave" object:nil];
}
ContactDetailTV
- (void)updateSubViewAfterSave:(NSNotification *)saveNotification
{
// Handling the "updateSubViewAfterSave"-notification by updating the table view that displays the data without using a fetchedResultsController
[self.tableView reloadData];
}
To update the object with the newest data from Core Data (because even when refetching the data, the context of the fetchedController keeps a "snapshot" of the object), I had to update the method.
- (void)updateSubViewAfterSave:(NSNotification *)saveNotification
{
// Handling the "updateSubViewAfterSave"-notification by updating the table view that displays the data without using a fetchedResultsController
id appDelegate = (id)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *tempContext = [appDelegate managedObjectContext];
[tempContext refreshObject:contact mergeChanges:YES];
[self.tableView reloadData];
}

CoreData autosaving and not loading all data after autosave

I have an NSPersistentDocument subclass using NSManagedObject subclasses for my data.
When a new document is opened, I do some initializing of data structures (trivial amount of populating fields). What I've noticed is that the Untitled document gets autosaved, and when the application re-opens, that document gets loaded. If the application quits, the user doesn't (by default) get prompted with the save dialog. If the window closes, the user does.
First question:
I want to call up the save dialog when the user quits the application. I don't want this Untitled document hanging around (under normal circumstances). I either want it saved or trashed.
I attempted to fill out:
- (void)applicationWillTerminate:(NSNotification *)aNotification
In order to trigger the document to be saved. Calling save: on the context at this point gives an error. From what I can tell, this is because the user hasn't yet saved the file on their own. In addition, calling [self close]; or [[self windowForSheet] close]; close the window without saving.
How can I force the save dialog to come up? How can I trash the untitled document?
Second question (no, I can't count):
Since when the application starts, there may or may not be an Untitled document to deal with, I'm trying to keep track of the state in another model. I've already found that the initial data (to which I referred earlier) is present when the Untitled document came up. My other model has some metadata, including a success flag/state for the populated data. Once the populated data is all in place and correct, the state indicates as such. Unfortunately, while my populated data is being loaded when the app starts with a pre-existing Untitled document, the metadata class is not.
Please excuse the roughness of the code, at this point, I'm mucking it up until I can see that it's working how I want before I polish it back off:
- (bool) createGameState {
NSEntityDescription* description = [NSEntityDescription entityForName:[GameState name] inManagedObjectContext:[self managedObjectContext]];
NSFetchRequest* req = [[NSFetchRequest alloc] init];
[req setEntity:description];
NSError *error = nil;
NSArray *array = [[self managedObjectContext] executeFetchRequest:req error:&error];
[req release];
req = nil;
GameState* result = nil;
if (array) {
NSUInteger count = [array count];
if (!count) {
// Create the new GameState.
DebugLog(#"Creating GameState");
result = [NSEntityDescription insertNewObjectForEntityForName:[GameState name] inManagedObjectContext:[self managedObjectContext]];
[result setIsLoaded:[NSNumber numberWithBool:NO]];
} else {
if (count > 1) {
NSLog(#"WARNING: Potentially Corrupt Game State. found: %lu", count);
}
result = [array objectAtIndex:0];
if ([result isLoaded]) {
[self variantLoaded];
} else {
// In this case, we have an aborted set-up. Since the game isn't
// playable, just refuse to create the GameState. This will
// force the user to create a new game.
return NO;
}
}
} else {
DebugLog(#"error: %#", error);
}
[game setState:result];
return result;
}
Note that array is always present, and count is always zero. No, I'm not explicitly calling save: anywhere. I'm relying on the standard auto-save, or the user performing a save.
EDIT:
I installed the Core Data Editor app. It turns out the issue isn't on saving the data, but on loading it. (Note: Due to another issue, the app saves as binary when instructed to save as XML, which causes much head banging.)
I've broken it down to the simplest code, which should pick up all objects of type GameState in an array. It retrieves none, despite there clearly being objects of the appropriate type in the saved file:
NSManagedObjectContext* moc = [self managedObjectContext];
NSEntityDescription* entity = [NSEntityDescription entityForName:#"GameState" inManagedObjectContext:moc];
NSFetchRequest* req = [[NSFetchRequest alloc] init];
[req setEntity:entity];
NSError *error = nil;
NSArray *array = [moc executeFetchRequest:req error:&error];
Array is not null, but [array count] is 0.
At this point, I'm guessing it's something simple that I'm overlooking.
Second EDIT:
I added -com.apple.CoreData.SQLDebug 5 and saved as SQLite. The call to executeFetchRequest does not generate any debug logs. I do see the INSERT INTO ZGAMESTATE entry show up in the logs. It seems that executeFetchRequest is not getting passed to the backend.
Third EDIT (this one burns):
I created a new xcode project, using core data (as I had with the other). I copied just this one function (stubbing where necessary) and plopped a call to it in windowControllerDidLoadNib. In this new project, the code above works.
Found the problem.
I errantly was loading objects in Document's - (id) init call. Moved to windowControllerDidLoadNib (which is what I did in the test version) and it worked fine.

Why is my app crashing when I modify a Core Data relationship in an NSOperation subclass?

Background
I've got the following tree of objects:
Name Project
Users nil
John nil
Documents nil
Acme Project Acme Project <--- User selects a project
Proposal.doc Acme Project
12:32-12:33 Acme Project
13:11-13:33 Acme Project
...thousands more entries here...
The user can assign a group to a project. All descendants get set to that project.
This locks up the main thread so I'm using NSOperations.
I'm using the Apple approved way of doing this, watching for NSManagedObjectContextDidSaveNotification and merging into the main context.
The Problem
My saves have been failing with the following error:
Failed to process pending changes before save. The context is still dirty after 100 attempts. Typically this recursive dirtying is caused by a bad validation method, -willSave, or notification handler.
What I've Tried
I've stripped all the complexities of my app away, and made the simplest project I could think of. And the error still occurs. I've tried:
Setting the max number of operations on the queue to 1 or 10.
Calling refreshObject:mergeChanges: at several points in the NSOperation subclass.
Setting merge policies on the managed object context.
Build and Analyze. It comes up empty.
My Question
How do I set relationships in an NSOperation without my app crashing? Surely this can't be a limitation of Core Data? Can it?
The Code
Download my project: http://synapticmishap.co.uk/CDMTTest1.zip
Main Controller
#implementation JGMainController
-(IBAction)startTest:(id)sender {
NSManagedObjectContext *imoc = [[NSApp delegate] managedObjectContext];
JGProject *newProject = [JGProject insertInManagedObjectContext:imoc];
[newProject setProjectName:#"Project"];
[imoc save];
// Make an Operation Queue
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue setMaxConcurrentOperationCount:1]; // Also crashes with a higher number here (unsurprisingly)
NSSet *allTrainingGroupsSet = [imoc fetchAllObjectsForEntityName:#"TrainingGroup"];
for(JGTrainingGroup *thisTrainingGroup in allTrainingGroupsSet) {
JGMakeRelationship *makeRelationshipOperation = [[JGMakeRelationship alloc] trainGroup:[thisTrainingGroup objectID] withProject:[newProject objectID]];
[queue addOperation:makeRelationshipOperation];
makeRelationshipOperation = nil;
}
}
// Called on app launch.
-(void)setupLotsOfTestData {
// Sets up 10000 groups and one project
}
#end
Make Relationship Operation
#implementation JGMakeRelationshipOperation
-(id)trainGroup:(NSManagedObjectID *)groupObjectID_ withProject:(NSManagedObjectID *)projectObjectID_ {
appDelegate = [NSApp delegate];
imoc = [[NSManagedObjectContext alloc] init];
[imoc setPersistentStoreCoordinator:[appDelegate persistentStoreCoordinator]];
[imoc setUndoManager:nil];
[imoc setMergePolicy:NSMergeByPropertyStoreTrumpMergePolicy];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(mergeChanges:)
name:NSManagedObjectContextDidSaveNotification
object:imoc];
groupObjectID = groupObjectID_;
projectObjectID = projectObjectID_;
return self;
}
-(void)main {
JGProject *project = (JGProject *)[imoc objectWithID:projectObjectID];
JGTrainingGroup *trainingGroup = (JGTrainingGroup *)[imoc objectWithID:groupObjectID];
[project addGroupsAssignedObject:trainingGroup];
[imoc save];
trainingGroupObjectIDs = nil;
projectObjectID = nil;
project = nil;
trainingGroup = nil;
}
-(void)mergeChanges:(NSNotification *)notification {
NSManagedObjectContext *mainContext = [appDelegate managedObjectContext];
[mainContext performSelectorOnMainThread:#selector(mergeChangesFromContextDidSaveNotification:)
withObject:notification
waitUntilDone:YES];
}
-(void)finalize {
appDelegate = nil;
[[NSNotificationCenter defaultCenter] removeObserver:self];
imoc = nil;
[super finalize];
}
#end
#implementation NSManagedObjectContext (JGUtilities)
-(BOOL)save {
// If there's an save error, I throw an exception
}
#end
Data Model
Update 1
I've experimented some more, and even without the merge, the exception is still thrown. Just saving the managed object context in another thread after modifying a relationship is enough.
I have a shared persistent store coordinator with the app delegate. I've tried making a separate NSPersistentStoreCoordinator for the thread with the same URL as my data store, but Core Data complains.
I'd love to suggestions on how I can make a coordinator for the thread. The core data docs allude to there being a way of doing it, but I can't see how.
You are crossing the streams (threads in this case) which is very bad in CoreData. Look at it this way:
startTest called from a button (is IBAction, assuming button tap) on Main thread
Your for loop creates a JGMakeRelationship object using the initializer trainGroup: withProject: (this should be called init, and probably call super, but that's not causing this issue).
You create a new managed object context in the operation, on the Main thread.
Now the operation queue calls the operations "main" method from a worker thread (put a breakpoint here and you'll see it's not on the main thread).
Your app goes boom because you've accessed a Managed object Context from a different thread than the one you created it on.
Solution:
Initialize the managed object context in the main method of the operation.

Resources