Im a creating an app for my iPhone using coredata.
I have a viewcontroller with an object i want to save that object to my FavoriteViewController.
By clicking a button favorite I want my object to be save into the managedObjectContext but I'm getting the following error:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Attempted to perform another operation with a fetch already in progress'. *
My Code:
// Step 1: Create Object
Favorite * newFavorite = (Favorite*)[NSEntityDescription insertNewObjectForEntityForName:#"Favorite" inManagedObjectContext:managedObjectContext];
// Step 2: Set Properties
newFavorite.name = #"Company";
NSLog(#"%#",newFavorite);
// Step 3: Save Object
NSError *error = nil;
if (![newFavorite.managedObjectContext save:&error]) { // this is where the program crash
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
I am not sure what I'm doing wrong.
I am going to guess you have a UI element, such as a table, that activates a fetch when the UI is changed. For example, if you have a fetched results controller, any scrolling of the table can activate the fetched results controller's fetch.
You can't mutate a collection while iterating over that collection because the count of the iteration changes while the iteration is in process. A fetch iterates over the collection of objects matching its entity and predicate. If you insert an object while the fetch is working you will get an error.
Usually you see this problem with multiple threads but I think the UI might trigger the problem in the right set of circumstances.
You have probably changed something in your tables. If this is true, try to use the original values for the table Z_METADATA (Z_VERSION, Z_UUID, Z_PLIST), Z_PRIMARYKEY (Z_ENT)...
Related
This is the code I'm using to insert a new object into my Raza entity. Nothing special though. I've used this delegate method and syntax everywhere accross my app, but for some reason I can not do it on this certain entity.
-(void)guardarRaza:(NSString *)nombre guardar:(BOOL)guardar{
if (guardar) {
Raza *raza = [NSEntityDescription insertNewObjectForEntityForName:#"Raza"
inManagedObjectContext:self.managedObjectContext];
raza.nombre = nombre;
NSError *error;
if (![self.managedObjectContext save:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
[self dismissModalViewControllerAnimated:YES];
}
The problem might come from the sqlite, because we're using one version to prepopulating the started version. I've followed the raywenderlich method entitled "Core Data Tutorial: How To Preload/Import Existing Data" .
It really works smoothly with other entities, however when I insert and save on that particular one, it crashes with CoreData: error: (19) PRIMARY KEY must be unique. I've checked out the sqlite, inserted and deleted new rows manually and it just works.
Does anyone have the same problem, or rather know how to fix it.
Thanks in advance
I just experienced this same error and found a solution that I have detailed in this other SO post:
Why is this code raising the "CoreData: error: (19) PRIMARY KEY must be unique" error?
I'm using this simple code for my fetch request
NSArray *fetchResults = [moc executeFetchRequest:request error:&error];
NSLog(#" i want show my result : %#",fetchResults); -> cause SIGABRT
If i'm using on my persistent store just after this creation, i have an error.
PS: the store was save between the populate and the request.
But if i close the app, and reopen ( in this case the store exist), i have no error.
in some case i can view this message : terminate called after throwing an instance of 'NSException'
but i can't access to this exception.
if i count the fetch results, i have a good number, it's really strange.
Thanks for help.
Okay, I have found the problem!
In the populate code, one of my relationships was insert with an autorelease.
Remove this, and now it's OK.
This is not a good solution:
NSManagedObject *relationEntity = [[NSEntityDescription insertNewObjectForEntityForName:#"picture" inManagedObjectContext:moc] autorelease];
Simply remove autorelease:
NSManagedObject *relationEntity = [NSEntityDescription insertNewObjectForEntityForName:#"picture" inManagedObjectContext:moc];
I have forgotten this in core data (don't use release, just set object to nil)!
I have an application that downloads an xml file, parses the file, and creates core data objects while doing so. In the parse code I have a function called 'emptydatacontext' that removes all items from Core Data before creating replacements items from the xml data. This method looks like this:
-(void) emptyDataContext
{
NSFetchRequest * allCon = [[NSFetchRequest alloc] init];
[allCon setEntity:[NSEntityDescription entityForName:#"Condition" inManagedObjectContext:managedObjectContext]];
NSError * error = nil;
NSArray * conditions = [managedObjectContext executeFetchRequest:allCon error:&error];
DebugLog(#"ERROR: %#",error);
DebugLog(#"RETRIEVED: %#", conditions);
[allCon release];
for (NSManagedObject * condition in conditions) {
[managedObjectContext deleteObject:condition];
}
// Update the data model effectivly removing the objects we removed above.
//NSError *error;
if (![managedObjectContext save:&error]) {
DebugLog(#"%#", [error domain]);
}
}
The first time this runs it deletes all objects and functions as it should - creating new objects from the xml file. I created a 'update' button that starts the exact same process of retrieving the file the proceeding with the parse & build. All is well until its time to delete the core data objects. This 'deleteObject' call creates a "EXC_BAD_ACCESS" error each time. This only happens on the second time through.
Captured errors return null. If I log the 'conditions' array I get a list of NSManagedObjects on the first run. On the second this log request causes a crash exactly as the deleteObject call does.
I have a feeling it is something very simple I'm missing or not doing correctly to cause this behavior. The data works great on my tableviews - its only when trying to update I get the crashes.
I have spent days & days on this trying numerous alternative methods. Whats left of my hair is falling out. I'd be willing to ante up some cash for anyone willing to look at my code and see what I'm doing wrong. Just need to get past this hurdle.
Thanks in advance for the help!
Did you save the context after removing the objects in the for loop? Be aware that deleteObject: does not delete the object immediately, it simply schedule it for deletion when changes are committed, i.e., when you save the context.
EDIT: Your problem may be related to how you present your data to the user on your table view. Without additional code is difficult to tell exactly (are you using NSFetchedResultsController or not?), but my guess is that the interaction between deleting the data and showing them on the table is not correct. Probably, what is happening is that your table is told to visualize your data, but then, when you delete them, you are not updating correctly the table.
Wow, so after a few days of testing I went down a path that led me to tracking down Zombies & memory mgmt. This was a situation where the errors I was recieving were leftover from issues in another area. When parsing the data and placing it into Core Data I released an object that was to be autoreleased. So any subsequent calls to that item within core data (save, deletec, etc) caused a crash. Thank you all for your help and I vow to pay closer attention with my memory mgmt calls.
I'm working on a piece of code for an iPhone application that fetches a bunch of data from a server and builds objects from it on the client. It ends up creating roughly 40,000 objects. They aren't displayed to the user, I just need to create instances of NSManagedObject and store them to persistent storage.
Am I wrong in thinking that the only way to do this is to create a single object, then save the context? is it best to create the objects all at once, then somehow save them to the context after they're created and stored in some set or array? If so, can one show some example code for how this is done or point me in the direction to code where this is done?
The objects themselves are relatively straight forward models with string or integer attributes and don't contain any complex relationships.
In any case, don't save after inserting every object, or be prepared for dreadful performances.
Here is the code I use to populate a Core Data repository upon first launch.
#define MAX_UNSAVED_AIRPORTS_BEFORE_SAVE 1000
int numAirports = 0;
int numUnsavedAirports = MAX_UNSAVED_AIRPORTS_BEFORE_SAVE; // *** bug. see below
for (NSDictionary *anAirport in initialAirports) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
Airport *newAirport = [NSEntityDescription insertNewObjectForEntityForName:#"Airport" inManagedObjectContext:managedObjectContext];
newAirport.city = [anAirport objectForKey:#"city"];
newAirport.code = [anAirport objectForKey:#"code"];
newAirport.name = [anAirport objectForKey:#"name"];
newAirport.country_name = [anAirport objectForKey:#"country_name"];
newAirport.latitude = [NSNumber numberWithDouble:[[anAirport objectForKey:#"latitude"] doubleValue]];
newAirport.longitude = [NSNumber numberWithDouble:[[anAirport objectForKey:#"longitude"] doubleValue]];
newAirport.altitude = [NSNumber numberWithDouble:[[anAirport objectForKey:#"altitude"] doubleValue]];
numAirports++;
numUnsavedAirports++;
if (numUnsavedAirports >= MAX_UNSAVED_AIRPORTS_BEFORE_SAVE) {
if (![managedObjectContext save:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
numUnsavedAirports = 0;
}
[pool release];
}
Also don't forget to save one last time after the loop.
Also be aware that a bug exists that will lead to a crash if all three of the following conditions are met:
The Repository is empty
You have a UITableView with sections
Your first save saves more than one object.
The workaround in the code above is to initialize the numUnsavedAirports to MAX_UNSAVED_AIRPORTS_BEFORE_SAVE in order to make sure the first save happens after the first insert.
I hope this helps.
Saving after each object would produce very bad performance. You should have a balance of the saves perhaps every 100 (testing will determine the sweet spot) and then keep track of where you are at in the processing when the user quits.
You get time on exit to store state so you can easily store your position in the data processing (5 blocks of 100 saved) and pick back up where you left off.
Saving every object individually would hammer the disk and slow the app to a crawl.
It's probably better to create a single object and save the context.
You have 40k objects. Let's say that creating a single NSManagedObject takes x time units. 40kx time units is probably measurable. While the object creation is happening, the user may quit the app for some reason; users are unpredictable. The next time your app starts, you go through the process all over again. It would not be desirable to create the 39,999th object only to have the user quit the app and lose all that work.
If your app were to create each object and save, you could speed up this process a bit. The app starts up and checks to see if it was able to complete the task the last time it ran. If the task was incomplete, it could try to pick up where it left off.
The single object creation and save method may take a longer time to complete but will have a greater likelihood of completing the task.
In terms of memory consumption, this also minimizes the in memory state of your app. The context isn't tracking 40k objects in memory.
I have a UITableViewController managing a grouped tableView. The tableView is populated from a fetchedResultsController.
If I click the Edit button in the NavigationBar, then select a row and click the Delete button, the row is deleted and all ends well.
However, if I swipe to reveal the Delete button in a row and click the Delete button, the app crashes with the following error:
2010-01-06 15:25:18.720 Take10[14415:20b] Serious application error. Exception was caught during Core Data change processing: -[NSCFArray objectAtIndex:]: index (1) beyond bounds (1) with userInfo (null)
2010-01-06 15:25:18.721 Take10[14415:20b] Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[NSCFArray objectAtIndex:]: index (1) beyond bounds (1)'
Of course, the index number in the error changes depending on the number of rows in the section where I am attempting to delete a row, and that number is 1 more than the number of remaining rows in the table section after the attempted delete.
Here is the code where I attempt to delete the data from the fetchedResultsController. The same method responds to both scenarios, so I don't understand why it crashes when swiping.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the managed object for the given index path
NSManagedObjectContext *context = [fetchedResultsController managedObjectContext];
[context deleteObject:[fetchedResultsController objectAtIndexPath:indexPath]];
// Save the context.
NSError *error = nil;
if (![context 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. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.
*/
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
}
Any ideas???
Thanks
Jk
Jeff LaMarche has done some good work with NSFetchedResultsController.
Try using his template here:
http://iphonedevelopment.blogspot.com/2010/01/navigation-based-core-data-application.html
And see if that solve your issue.
One difference between normal and swipe delete is that the latter will call tableView:willBeginEditingRowAtIndexPath and tableView:didEndEditingRowAtIndexPath. In fact, that's a good way of suppressing the indentation and showing of an insert row.
Another difference is that setEditing: is called (with NO as the parameter value) immediately after the delete.
Set breakpoints in any of those three functions that you've defined/overridden and see if you can narrow down where it's happening.