Prevent Core Data persisting object - objective-c

Is it possible to prevent Core Data to persist an object?
Let's say if I have a NSManagedObject subclass with a BOOL property isTemporary.
So when a save is called on the context I would do a check on the object:
if (self.isTemporary) {
// Do not save
} else {
// Save this object
}
EDIT: More background information for the issue
Hmm to clarify my issue, I create an object, if it exists already I'm the db I fetch it, if it doesn't exist I insert it and set the the temporary flag of the object to YES. I set the flag because it's not clear at this stage if the user will perform a save or cancel action. If he saves I set the flag temporary to NO. If he cancels then I delete the object if temporary flag is YES.
So far so good, but in the meantime in the background there can occur core data save operations in the background that will persist these objects even though I don't want them persisted (because they should be temporary). So if I'm unlucky and app is killed I could have unwanted objects that have the temporary flag set to YES. One option would be to perform a clean operation on startup of the app to remove objects with temporary flag YES. But everything would be a whole lot easier if it would not persist those objects.

Correct, that is how it can be done.
if (self.isTemporary && self.managedObjectContext) {
[self.managedObjectContext delete:self];
}
Note that the managedObjectContext of a NSManagedObject becomes nil once it is deleted.

If you want to prevent Core Data from persisting your ManagedObject, in other words if you don't want your object to be written in the file, you can achieve this by initializing your ManagedObject in following way:
#implementation MyManagedObject
- (id) init
{
NSEntityDescription* entity = [NSEntityDescription entityForName:#"MyEntity" inManagedObjectContext:managedObjectContext];
self = [super initWithEntity:entity insertIntoManagedObjectContext:Nil];
}
#end
Now if you want to save your object, save it in following way:
[managedObjectContext insertObject:myManagedObject];
NSError* error;
if ([managedObjectContext save:&error]) {
NSLog(#"Successfully saveed ManagedObject!");
} else {
NSLog(#"Failed to save ManagedObject!");
}
Hope this helps!

Related

Adding managedObjectContext objects with an NSArrayController

I have this application that is using core data and an NSArrayController to manage some objects in a table. I have the code below to pick up some objects on a directory. My questions is about the section below labeled "Handle Files". I create a new Video object using the url, I copy the metadata attributes using a custom function I wrote. The object is now inserted in the managedObjectContext. My question is, since I have my NSArrayController bound to my managedObjectContext, why do I have to still do [self addObject:newVideo] to have the object shown on my table? Is there a way to force the array controller to pull the object from the managedObjectContext without having to manually add it? It will be a hassle having to be updating both things every time I add or remove an object.
for (NSURL *url in _dirEnumerator) {
NSNumber *_isDirectory = nil;
[url getResourceValue:&_isDirectory forKey:NSURLIsDirectoryKey error:NULL];
if (![_isDirectory boolValue]) {
if (([_mediaTypes containsObject:[[url pathExtension]uppercaseString]])) {
// Handle the files
Video *newVideo = [NSEntityDescription insertNewObjectForEntityForName:#"Video" inManagedObjectContext:_managedObjectContext];
[newVideo copyAttributesFrom:url];
[self addObject:newVideo];
NSLog(#"Inserting video: %#",[newVideo valueForKey:#"name"]);
}
}
}
Well, I had my bindings all wrong an the array controller was not feeding my table correctly. You cannot sneak objects behind the array controller, if you implement the array controller you must let him do his job and that includes adding and removing objects. He will take care of letting the tableview know when things have changed.

Objects not being removed from NSOutlineView/ManagedObjectContext

I have an NSTreeController that is getting objects adding to it addObject: method.
I have an NSOutlineView which has its content bound to the NSTreeController's arrangedObjects.
When I delete an object, as below
- (void) deleteSelectionConfirmed {
id selectedItem = [_outlineView itemAtRow:[_outlineView selectedRow]];
id obj = ((NSTreeNode *)selectedItem).representedObject;
NSManagedObjectContext *context = [self managedObjectContext];
[context deleteObject:obj];
NSError *error;
NSLog(#"%hhd", [[self managedObjectContext] hasChanges]);
if (![context save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
} else {
NSLog(#"%#", [_hostController content]);
[_outlineView reloadData];
}
NSLog(#"%hhd", [[self managedObjectContext] hasChanges]);
}
It doesn't seem to be deleted.
The print out of my treeController content looks as such.
"<Host: 0x6080000b41c0> (entity: Host; id: 0x608000034180 <x-coredata://6E3284F6-D870-4DAF-A4E5-B6A4EB75021E/Host/p131> ; data: {\n hostname = asdfasdf;\n index = 0;\n children = \"<relationship fault: 0x60800022ecc0 'children'>\";\n title = gggg;\n username = asdf;\n})",
"<Host: 0x6080002a0960> (entity: Host; id: 0x608000032a80 <x-coredata://6E3284F6-D870-4DAF-A4E5-B6A4EB75021E/Host/p133> ; data: <fault>)"
)
The second line is the 'deleted' object. This is causing issues with the NSOutlineView to display a blank row. If I close an re-open my app however, dumping an NSFetchRequest shows my Managed object was actually deleted, and my NSOutlineView has the intended rows.
Am I missing something? Am I adding objects the wrong way? Should i be using a different binding? Any help is greatly appreciated.
Set the tree controller to automatically prepare its content (using the moc, the entity name, and the fetch predicate). If you do that, you presumably want to remove that code from -applicationDidFinishLaunching:.
From Core Data Programming Guide: Troubleshooting Core Data:
Table view or outline view contents not kept up-to-date when bound to > an NSArrayController or NSTreeController object
Problem: You have a table view or outline view that displays a
collection of instances of an entity. As new instances of the entity
are added and removed, the table view is not kept in sync.
Cause: If the controller's content is an array that you manage
yourself, then it is possible you are not modifying the array in a way
that is key-value observing compliant.
If the controller's content is fetched automatically, then you have
probably not set the controller to "Automatically prepare content."
Alternatively, the controller may not be properly configured.
Remedy: If the controller's content is a collection that you manage
yourself, then ensure you modify the collection in a way that is
key-value observing compliant—see “Troubleshooting Cocoa Bindings”.
If the controller's content is fetched automatically, set the
"Automatically prepares content" switch for the controller in the
Attributes inspector in Interface Builder (see also
automaticallyPreparesContent). Doing so means that the controller
tracks inserts into and deletions from its managed object context for
its entity.
If neither of these is a factor, check to see that the controller is
properly configured (for example, that you have set the entity
correctly).
So, the tree controller does not track insertions into and deletions from the managed object context unless it is set to automatically prepare content.

Can’t undo more than one operation

When I call undo on the context following deletion of a single object, all works as expected. But if user deletes an object, then deletes another object, undo will work only to restore the second object, no matter how many times user requests undo, as though undoLevels were set to 1. This happens whether undoLevels is at the default of 0 (unlimited) or is explicitly set to 6 as a test.
Furthermore, if a single action deletes multiple objects, calling undo afterward has no effect; none of the objects is restored. I tried explicitly bracketing the deletion loop with begin/endUndoGrouping, to no avail. The undoManager’s groupsByEvent is YES (by default), but it makes no difference whether I call a straight undo or undoNestedGroup.
Is the context somehow being saved after each operation? No, because if I quit and relaunch the app after running these tests, all objects are still present in the database.
What am I missing?
OK, you want code. Here’s what I imagine is most relevant:
Context getter:
- (NSManagedObjectContext *) managedObjectContextMain {
if (managedObjectContextMain) return managedObjectContextMain;
NSPersistentStoreCoordinator *coordinatorMain = [self persistentStoreCoordinatorMain];
if (!coordinatorMain) {
// present error...
return nil;
}
managedObjectContextMain = [[NSManagedObjectContext alloc] init];
[managedObjectContextMain setPersistentStoreCoordinator: coordinatorMain];
// Add undo support. (Default methods don't include this.)
NSUndoManager *undoManager = [[NSUndoManager alloc] init];
// [undoManager setUndoLevels:6]; // makes no difference
[managedObjectContextMain setUndoManager:undoManager];
[undoManager release];
// ...
return managedObjectContextMain;
}
Multiple-object deletion method (called by a button on a modal panel):
/*
NOTE FOR SO:
SpecialObject has a to-one relationship to Series.
Series has a to-many relationship to SpecialObject.
The deletion rule for both is Nullify.
Series’ specialObject members need to be kept in a given order. So Series has a transformable attribute, an array of objectIDs, used to prepare a transient attribute, an array of specialObjects, in the same order as their objectIDs.
*/
- (void) deleteMultiple {
Flixen_Foundry_AppDelegate *appDelegate = [[NSApplication sharedApplication] delegate];
NSManagedObjectContext *contextMain = [appDelegate managedObjectContextMain];
NSUndoManager *undoMgr = [contextMain undoManager];
[undoMgr beginUndoGrouping];
// Before performing the actual deletion, drop the seln in the locator table.
[appDelegate.objLocatorController.tvObjsFound deselectAll:self];
// Get the indices of the selected objects and enumerate through them.
NSIndexSet *selectedIndices = [appDelegate.objLocatorController.tvObjsFound selectedRowIndexes];
NSUInteger index = [selectedIndices firstIndex];
while (index != NSNotFound) {
// Get the obj to be deleted and its series.
SpecialObject *sobj = [appDelegate.objLocatorController.emarrObjsLoaded objectAtIndex:index];
Series *series = nil;
series = sobj.series;
// Just in case...
if (!series) {
printf("\nCESeries' deleteMultiple was called when Locator seln included objs that are not a part of a series. The deletion loop has therefore aborted.");
break;
}
// Get the obj's series index and delete it from the series.
// (Series has its own method that takes care of both relnshp and cache.)
NSUInteger uiIndexInSeries = [series getSeriesIndexOfObj:sobj];
[series deleteObj:sobj fromSeriesIndex:uiIndexInSeries];
// Mark the special object for Core Data deletion; it will still be a non-null object in emarrObjsLoaded (objLocatorController’s cache).
[contextMain deleteObject:sobj];
// Get the next index in the set.
index = [selectedIndices indexGreaterThanIndex:index];
}
[undoMgr endUndoGrouping];
// Purge the deleted objs from loaded, which will also reload table data.
[appDelegate.objLocatorController purgeDeletedObjsFromLoaded];
// Locator table data source has changed, so reload. But end with no selection. (SeriesBox label will have been cleared when Locator seln was dropped.)
[appDelegate.objLocatorController.tvObjsFound reloadData];
// Close the confirm panel and stop its modal session.
[[NSApplication sharedApplication] stopModal];
[self.panelForInput close];
}
Here’s the Series method that removes the object from its relationship and ordered cache:
/**
Removes a special object from the index sent in.
(The obj is removed from objMembers relationship and from the transient ordered obj cache, but it is NOT removed from the transformable array of objectIDrepns.)
*/
- (void) deleteObj:(SpecialObject *)sobj fromSeriesIndex:(NSUInteger)uiIndexForDeletion {
// Don't proceed if the obj is null or the series index is invalid.
if (!sobj)
return;
if (uiIndexForDeletion >= [self.emarrObjs count])
return;
// Use the safe Core Data method for removing the obj from the relationship set.
// (To keep it private, it has not been declared in h file. PerformSelector syntax here prevents compiler warning.)
[self performSelector:#selector(removeObjMembersObject:) withObject:sobj];
// Remove the obj from the transient ordered cache at the index given.
[self.emarrObjs removeObjectAtIndex:uiIndexForDeletion];
// But do NOT remove the obj’s objectID from the transformable dataObjIDsOrdered array. That doesn't happen until contextSave. In the meantime, undo/cancel can use dataObjIDsOrdered to restore this obj.
}
Here’s the method, and its follow-up, called by comm-z undo:
- (void) undoLastChange {
Flixen_Foundry_AppDelegate *appDelegate = [[NSApplication sharedApplication] delegate];
NSManagedObjectContext *contextMain = [appDelegate managedObjectContextMain];
// Perform the undo. (Core Data has integrated this functionality so that you can call undo directly on the context, as long as it has been assigned an undo manager.)
// [contextMain undo];
printf("\ncalling undo, with %lu levels.", [contextMain.undoManager levelsOfUndo]);
[contextMain.undoManager undoNestedGroup];
// Do cleanup.
[self cleanupFllwgUndoRedo];
}
- (void) cleanupFllwgUndoRedo {
Flixen_Foundry_AppDelegate *appDelegate = [[NSApplication sharedApplication] delegate];
NSManagedObjectContext *contextMain = [appDelegate managedObjectContextMain];
DataSourceCoordinator *dataSrc = appDelegate.dataSourceCoordinator;
// ...
// Rebuild caches of special managed objects.
// (Some managed objects have their own caches, i.e. Series' emarrObjs. These need to be refreshed if their membership has changed. There's no need to use special trackers; the context keeps track of these.)
for (NSManagedObject *obj in [contextMain updatedObjects]) {
if ([obj isKindOfClass:[Series class]] && ![obj isDeleted])
[((Series *)obj) rebuildSeriesCaches];
}
// ...
// Regenerate locator's caches.
[appDelegate.objLocatorController regenerateObjCachesFromMuddies]; // also reloads table
}
Here’s the series method that regenerates its caches following undo/awake:
- (void) rebuildSeriesCaches {
// Don't proceed if there are no stored IDs.
if (!self.dataObjIDsOrdered || [self.dataObjIDsOrdered count] < 1) {
// printf to alert me, because this shouldn’t happen (and so far it doesn’t)
return;
}
NSMutableArray *imarrRefreshedObjIdsOrdered = [NSMutableArray arrayWithCapacity:[self.objMembers count]];
NSMutableArray *emarrRefreshedObjs = [NSMutableArray arrayWithCapacity:[self.objMembers count]];
// Loop through objectIDs (their URIRepns) that were stored in transformable dataObjIDsOrdered.
for (NSURL *objectIDurl in self.dataObjIDsOrdered) {
// For each objectID repn, loop through the objMembers relationship, looking for a match.
for (SpecialObject *sobj in self.objMembers) {
// When a match is found, add the objectID repn and its obj to their respective replacement arrays.
if ([[sobj.objectID URIRepresentation] isEqualTo:objectIDurl]) {
[imarrRefreshedObjIdsOrdered addObject:objectIDurl];
[emarrRefreshedObjs addObject:sobj];
break;
}
// If no match is found, the obj must have been deleted; the objectID repn doesn't get added to the replacement array, so it is effectively dropped.
}
}
// Assign their replacement arrays to the transformable and transient attrs.
self.dataObjIDsOrdered = imarrRefreshedObjIdsOrdered;
self.emarrObjs = emarrRefreshedObjs;
}
(I’ve omitted the Locator’s regenerateObjCachesFromMuddies because, although I am using its table to view the results of the deletion and undo, I can reload the table with a new fetch, completely regenerating the table’s caches, and this test still shows that the undo isn’t working.)
As usual, just the task of putting together a SO question helps solve the problem, and I realize now that undo works fine as long as I’m working with simple objects that don’t involve the reciprocal SpecialObject-Series relationship. I’m doing something wrong there...
I think you're getting into a fight with custom undo stuff and Core Data's automagic support.
In normal undo/redo code, you have undoable funnel points. Usually an undoable add and its inverse undoable remove. Calling one registers the other as the inverse action and vice-versa. User undo/redo then just goes back and forth between them. You separate your "user created a new Foo" code from your "now add this foo to the collection undoably" code (that way "remove Foo" and "add Foo" work independently of supplying a newly-created Foo).
With Core Data, add and remove means "insert into the context and remove from the context". Also, you still need custom funnel methods because (in your case), you're doing some additional stuff (updating a cache). This is easy enough to do with a Foo, but what happens when you want to manipulate the relationship between a Foo/Bar assembly that gets created in one action?
If creating a Foo created a few Bars with it, it'd be one thing (-awakeFromInsert and the like) since you'd only have to deal with updating your caching (which you could do, by the way, through key/value observing the context for changes). Since creating a Foo seems to establish relationships with existing Bars (which are already in the context), you run into a difficult wall when trying to cooperate with CD's built-in undo support.
There is no easy solution in this case if you're using the built-in Core Data undo/redo support. In this case, you can do as this post suggests and turn it off. You can then handle undo/redo entirely yourself ... but you'll have a lot of code to write to observe your objects for changes to interesting attributes, registering the inverse action for each.
While it isn't a solution to your problem, I hope it at least points out the complexity of what you're trying to do and gives you a possible path forward. Without knowing a LOT more about your model (at the conceptual level at least) and how your UI presents it to the user, it's hard to give specific architectural advice.
I hope I'm wrong about this case - maybe someone else can give you a better answer. :-)
It turns out that you can have Foo creation that involves changing relationships with pre-existing Bars, and custom caches, and NSUndoManager can still handle it all — but with a kink: You have to save the context after each such change; otherwise the undo manager will cease to function.
Since undo can actually reach back to states before the save, this is not such a bad thing. It does complicate matters if you want the user to be able to revert to the state when they last chose to save, but that can be handled by making a copy of the database whenever the user chooses to save.
So in the deleteMultiple method, following the while deletion loop, I added a call to save the context.
There’s another error in my scheme, which is that I erroneously thought NSUndoManager would ignore transformable attributes. Well, obviously, since transformable attrs are persisted, they are tracked by the persistentStoreCoordinator and are therefore included in undo operations. So when I failed to update the xformable attr array upon deletion, thinking I would need its info for restoration in the event of undo, I was ruining the action/inverse-action symmetry.
So in the deleteObject:fromSeriesIndex method, the Series method that handles the caches, I added this code, updating the transformable ObjectID array:
NSMutableArray *emarrRemoveID = [self.dataObjIDsOrdered mutableCopy];
[emarrRemoveID removeObjectAtIndex:uiIndexForDeletion];
self.dataObjIDsOrdered = emarrRemoveID;
[emarrRemoveID release];
(My assumption that the NSUndoManager would ignore the transient cache was correct. The call to rebuildSeriesCaches in cleanupFllwgUndoRedo takes care of that.)
Undo now works, both for simple objects and for objects in SpecialObject-Series relationships. The only remaining problem is that it takes more than one command-Z to happen. I’ll have to experiment more with the groupings…
EDIT: It isn’t necessary to save the context post-deletion if the managed object’s custom caches are handled correctly:
1) The caches should NOT be rebuilt following undo. The undo manager will take care of this on its own, even for the transient cache, as long as the transient property is included in the managed object model.
2) When changing the NSMutableArray cache (emarrObjs), using removeObjectAtIndex alone will confuse the undo manager. The entire cache must be replaced, the same way it is with the NSArray cache dataObjIDsOrdered.

What's the way to communicate a set of Core Data objects stored in the background to the main thread?

Part of my iOS project polls a server for sets of objects, then converts and saves them to Core Data, to then update the UI with the results. The server tasks happens in a collection of NSOperation classes I call 'services' that operate in the background. If NSManagedObject and its ~Context were thread safe, I would have had the services call delegate methods on the main thread like this one:
- (void)service:(NSOperation *)service retrievedObjects:(NSArray *)objects;
Of course you can't pass around NSManagedObjects like this, so this delegate method is doomed. As far as I can see there are two solutions to get to the objects from the main thread. But I like neither of them, so I was hoping the great StackOverflow community could help me come up with a third.
I could perform an NSFetchRequest on the main thread to pull in the newly added or modified objects. The problem is that the Core Data store contains many more of these objects, so I have to add quite some verbosity to communicate the right set of objects. One way would be to add a property to the object like batchID, which I could then pass back to the delegate so it would know what to fetch. But adding data to the store to fix my concurrency limitations feels wrong.
I could also collect the newly added objects' objectID properties, put them in a list and send that list to the delegate method. The unfortunate thing though is that I have to populate the list after I save the context, which means I have to loop over the objects twice in the background before I have the correct list (first time is when parsing the server response). Then I still only have a list of objectIDs, which I have to individually reel in with existingObjectWithID:error: from the NSManagedObjectContext on the main thread. This just seems so cumbersome.
What piece of information am I missing? What's the third solution to bring a set of NSManagedObjects from a background thread to the main thread, without losing thread confinement?
epologee,
While you obviously have a solution you are happy with, let me suggest that you lose some valuable information, whether items are updated, deleted or inserted, with your mechanism. In my code, I just migrate the userInfo dictionary to the new MOC. Here is a general purpose routine to do so:
// Migrate a userInfo dictionary as defined by NSManagedObjectContextDidSaveNotification
// to the receiver context.
- (NSDictionary *) migrateUserInfo: (NSDictionary *) userInfo {
NSMutableDictionary *ui = [NSMutableDictionary dictionaryWithCapacity: userInfo.count];
NSSet * sourceSet = nil;
NSMutableSet *migratedSet = nil;
for (NSString *key in [userInfo allKeys]) {
sourceSet = [userInfo valueForKey: key];
migratedSet = [NSMutableSet setWithCapacity: sourceSet.count];
for (NSManagedObject *mo in sourceSet) {
[migratedSet addObject: [self.moc objectWithID: mo.objectID]];
}
[ui setValue: migratedSet forKey: key];
}
return ui;
} // -migrateUserInfo:
The above routine assumes it is a method of a class which has an #property NSManagedObjectContext *moc.
I hope you find the above useful.
Andrew
There's a section of the Core Data Programming Guide that addresses Concurrency with Core Data. In a nutshell, each thread should have its own managed object context and then use notifications to synchronize the contexts.
After a little experimentation, I decided to go for a slight alteration to my proposed method number 2. While performing background changes on the context, keep a score of the objects you want to delegate back to the main thread, say in an NSMutableArray *objectsOfInterest. We eventually want to get to the objectID keys of all the objects in this array, but because the objectID value changes when you save a context, we first have to perform that [context save:&error]. Right after the save, use the arrayFromObjectsAtKey: method from the NSArray category below to generate a list of objectID instances, like so:
NSArray *objectIDs = [objectsOfInterest arrayFromObjectsAtKey:#"objectID"];
That array you can pass back safely to the main thread via the delegate (do make sure your main thread context is updated with mergeChangesFromContextDidSaveNotification by listening to the NSManagedObjectContextDidSaveNotification). When you're ready to reel in the objects of the background operation, use the existingObjectsWithIDs:error: method from the category below to turn the array of objectID's back into a list of working NSManagedObjects.
Any suggestions to improve the conciseness or performance of these methods is appreciated.
#implementation NSArray (Concurrency)
- (NSArray *)arrayFromObjectsAtKey:(NSString *)key {
NSMutableArray *objectsAtKey = [NSMutableArray array];
for (id value in self) {
[objectsAtKey addObject:[value valueForKey:key]];
}
return objectsAtKey;
}
#end
#implementation NSManagedObjectContext (Concurrency)
- (NSArray *)existingObjectsWithIDs:(NSArray *)objectIDs error:(NSError **)error {
NSMutableArray *entities = [NSMutableArray array];
#try {
for (NSManagedObjectID *objectID in objectIDs) {
// existingObjectWithID might return nil if it can't find the objectID, but if you're not prepared for this,
// don't use this method but write your own.
[entities addObject:[self existingObjectWithID:objectID error:error]];
}
}
#catch (NSException *exception) {
return nil;
}
return entities;
}
#end

Error when deleting Core Data object: NSManagedObjectContext cannot delete objects in other contexts

I am encountering an issue when attempting to remove an object from my Core Data store. The error I receive when calling deleteOject is as follows: An NSManagedObjectContext cannot delete objects in other contexts.
I have found some documentation regarding this error online but it mostly relates to accessing the ManagedObjectContext in multiple threads which can cause issues, but I am not currently working on any other threads. I have gone through my code in an attempt to ensure that I am not creating any other context except for the one that I create in my AppDelegate and can not find a likely culprit.
The code that I am testing with is below:
NSMutableSet *remoteNids = [NSMutableSet setWithObjects:#"140", #"141", nil];
for (GCEvent *event in nodeEventsFromStore) {
if (![remoteNids containsObject:event]) {
NSLog(#"Event no longer exists on remote. Removing object %# from store.", event);
[[delegate managedObjectContext] deleteObject:event];
}
else {
NSLog(#"Event %# exists on remote", event);
}
}
It's highly unlikely that Core Data is lying to you. I would suggest putting an assert in your code like this:
NSAssert([delegate managedObjectContext] == [event managedObjectContext], #"Found two contexts: %# and %#", [delegate managedObjectContext, [event managedObjectContext]);
Run your app via Xcode with breakpoints on (so it breaks when you hit the assertion) and set MallocStackLoggingNoCompact=YES.
When the assert is triggered, you can use the gdb console like so:
(gdb) info malloc [address of event's MOC]
(gdb) info malloc [address of delegate MOC]
That will print the alloc stack trace and show you where you created the two moc's.
The simplest way to troubleshoot this would be to log the managed object context and then the managedObjectContext property of each GCEvent object before you delete it. If the error is correct the address of the two context will not match. If you don't have multiple context then the managedObjectContext property of each GCEvent is most likely nil.
You can get this error if you intialize a managed object directly i.e. using alloc-init, but then never assign it to the context by setting its managedObjectContext property to a context. A managed object context will treat any value other than itself as being another context even if that is a nil value.