how to unregister an object in a managedObjectcontext? - iphone

Is there any method to do this job? I could not find in the document.

To delete an object from database use the following method:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
[managedObjectContext deleteObject:[userResults objectAtIndex:indexPath.row]];
[userResults removeObjectAtIndex:indexPath.row];
// Save the context.
NSError *error = nil;
if (![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. 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();
}
[self.tableView reloadData];
}
Where userResults is the NSMutableArray type, which you use to fetch data and store temporary values

If you mean deleting you can use [context deleteObject:object] where context is your NSManagedContext and object is your NSManagedObject derived object.

Related

Error after deleting NSManagedObject "The operation couldn’t be completed"

so I'm having a Core Data & iCloud combined Database that is working fine for me.
Some users though had a certain crash after trying to delete NSManagedObject's that are respresented in an UITableView. In the debug-logs that I attach to the crashreports there is the following error-log before the crash:
2013-05-08 22:38:51.851 MyApp[11819:907] Unresolved error Error Domain=NSCocoaErrorDomain Code=134030 "The operation couldn’t be completed. (Cocoa error 134030.)", {
}
Unfortunately the info about the error: {} is empty and I never had the error myself, so I don't know how to recreate it. It did occur at least 21 times for customers though.
(Whats even worse is, once they reopen the App, some NSManagedObject is in an invalid state and my tableview has problems loading it. But that just at the side)
The actual error is "SIGABRT" in " tableView:commitEditingStyle:forRowAtIndexPath:", here is the respective code:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
ICLog(#"Deleting the row %i,%i from the data source", indexPath.row, indexPath.section);
NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
[context deleteObject:[self.fetchedResultsController objectAtIndexPath:indexPath]];
//Save the context
NSError *error;
if (![context save:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
}
}
PS: From the Logs its also clear that this is all run from the main thread.
Anyone here who had this problem too?
UPDATE:
Luckily I had the same error in another App I'm working on just now, in a device I have access too (so far I had never saw this happening on any of my devices), so now I can investigate it while it happens. This is what I found out:
a) it still occurs after switching off icloud in the settings
b) the error occurs everytime I try to delete or insert any NSManagedObject, even if I do it in my Appdelegate right after starting, without ever accessing the data somewhere else
So it seems like something completely stops my databse from being updated. I can catch the above the exception, then the App at least isn't crashing. But of course no changes to the database are saved.
Errors on [context save:] often occur when you try to save an invalid state of your datamodel.
Since you have deleted an object before, i would suspect that this object was in a relation with some other object that still is present, but now has it's relation in an invalid state (nil for a relation that is non-optional)
Checking your delete rules could help, maybe some relation to the object you delete needs to be optional or you have to propagate the deletion correctly.
I will be making some assumptions here.
What I can see is that you delete the CoreData object using the object in fetchedResultsController but you never unset the object from whatever you use as your ViewController iVar fetch storage in numberOfRowsInSection and re-organise your table index after deletion.
Let say your original [indexPath.row count] is 7 then you go on and delete indexPath.row = 5 from your tableView. Then you go on and try to delete another object at indexPath.row = 6 that item exists on your tableView index because you havent re-organised the indexs, but it wont exist in CoreData.... given that now the max count is 6 that the index value is now 5 of what originally was 6.... This could be what your users are experiencing...
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [yourFetchedDataVariable count];
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
ICLog(#"Deleting the row %i,%i from the data source", indexPath.row, indexPath.section);
NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
[context deleteObject:[self.fetchedResultsController objectAtIndexPath:indexPath]];
//Save the context
NSError *error;
if (![context save:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
// the possible solution
[yourFetchedDataVariable removeObjectAtIndex:indexPath.row];
// also now you want to re-order your array index...
[yourTable reloadData]; // and re order your table indexses aswell
}
}
The optional relationship are you sure it is optional? and optional in what sense to Xcode? I ahd this issue once and it was due to a "optional" relationship.

New objects not saving properly when added to tableView

I'm having some issues getting Core Data to save new rows that I add when using a UITextField. Here is my method for inserting objects into my table view. What should happen is when I click the add button, a textfield should be added, and then go right into edit mode. Then when the user clicks done on the keyboard the textfield should end editing and then the textfield should save the entry into core data.
Edit: removed call to textFieldDidEndEditing in the insertNewObject:(id)sender method. It was crashing the app
- (void)insertNewObject:(id)sender {
NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
TehdaItem *item = [NSEntityDescription insertNewObjectForEntityForName:#"TehdaItem" inManagedObjectContext:context];
// If appropriate, configure the new managed object.
// Normally you should use accessor methods, but using KVC here avoids the need to add a custom class to the template.
// Putting the cell in edit mode
TehdaTableViewCell *editcell;
for (TehdaTableViewCell *cell in [self.tableView visibleCells]) {
if (cell.itemLabel.text == item.itemTitle) {
editcell = cell;
break;
}
}
[editcell.itemLabel becomeFirstResponder];
// The cell needs to call the method textfield did end editing so that it can save the new object into the store
// 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.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
Here is my textFieldDidEndEditing method:
- (void)textFieldDidEndEditing:(UITextField *)textField {
TehdaTableViewCell *cell = (TehdaTableViewCell *) textField.superview.superview;
TehdaItem *item = [self.fetchedResultsController objectAtIndexPath:[self.tableView indexPathForCell:cell]];
//TehdaTableViewCell *cell;
item.itemTitle = cell.itemLabel.text;
}
Not really sure where to go from here. Any help would be appreciated.
Thanks.
You can use
NSError *error;
[item.managedObjectContext save:&error];
if (error) {
// Triage the problem and respond appropriately
}
in the - (void)textFieldDidEndEditing:(UITextField *)textField method. But if I were you I'd do some validation before you save the object.

Why does releasing my CoreData context/entity make my app crash?

I have a simple CoreData app which allows you to add items to a list, displayed in a table view. When the user types in a new item, the following method is called:
- (void)addNewItem:(NSString *)item
{
// Create a new instance of the entity managed by the fetched results controller.
NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
NSEntityDescription *entity = [[self.fetchedResultsController fetchRequest] entity];
Item *newItem = [NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:context];
[newItem setName:item];
// Save the context.
NSError *error = nil;
if (![context save:&error])
{
//error handling code
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
[context release];
[entity release];
[newItem release];
The app always allows you to add one item to the list, but then crashes if you try to add a second. If I remove "[newItem release];", the app will allow you to add 4 list items, and then suddenly crash when you try to enter a fifth.
The app will only work properly if all three of those release statements at the end of the method are removed. Can anyone explain why?
The objects are all autoreleased (because you never alloc init anything), so you're not supposed to release them yourself. It's not predictable when your app will crash as far as I can tell, but it will eventually crash.
Just to clarify #BoltClock's answer. It's not about alloc, init only, but there's also new..., copy..., etc.
You should read Memory Management Guide, especially Memory Management Rules.

Handle row deletion in UITableViewController

I hava a UINavigationController. The first level is a UITableViewController, the second level just shows details on one of the items of the table view.
In this detail view, I can delete the item. It deletes the underlying managed object.
When I pop back to the view, I have a crash. I understand why, it's because I didn't update the cached array that contains the data.
I looked at several tutorials and I don't exactly understand how am I supposed to handle deletion. Maybe I don't understand exactly where I should fetch the objects in the model. Should I do a query for every cellForRowAtIndexPath and take the item in the result at position indexPath.row? It doesn't look efficient. Should I check for changes somewhere and recache the whole query in an array. I would think CoreData would provide something more natural but I couldn't find it so far.
Thanks in advance.
It is fairly simple. In the child view you should (really, really should) have a reference to the NSManagedObject you are working with. When you want to delete it then you just:
NSManagedObjectContext *moc = [[self myObject] managedObjectContext];
[moc deleteObject:[self myObject]];
NSError *error = nil;
if (![moc save:&error]) {
NSLog(#"Save failed: %#\n%#", [error localizedDescription], [error userInfo]);
}
This will delete the object. The parent, since it is using a NSFetchedResultsController (which you should also REALLY be doing) will take care of itself.
It seems somewhat non-standard deleting an item in the parent controller from your detail controller, but perhaps it makes sense in your case. I presume you know that you can directly delete items in the tableview. There are many example code projects from Apple which along with the docs should give you an idea how to do that.
To answer your question, you could create a property/variable in your detail controller's class which holds a reference to the tableview controller then send a message to that controller to handle the delete. Creating a protocol for this would be good style but not necessary. When the tableview class receives the delete item message, it updates the array, and when that view is redisplayed you should call reloadData on the tableview. This is the standard paradigm: make changes to your underlying data model and tell the tablview to reload.
In case you don't use NSFetchedResultsController, all you need to do is implement the following method:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the managed object at the given index path.
NSManagedObject *rowToDelete = [currentRows objectAtIndex:indexPath.row];
[managedObjectContext deleteObject:rowToDelete];
// Commit the change.
NSError *error;
if (![managedObjectContext save:&error]) {
// Handle the error.
NSLog(#"Failed to save to data store: %#", [error localizedDescription]);
NSArray* detailedErrors = [[error userInfo] objectForKey:NSDetailedErrorsKey];
if(detailedErrors != nil && [detailedErrors count] > 0) {
for(NSError* detailedError in detailedErrors) {
NSLog(#" DetailedError: %#", [detailedError userInfo]);
}
}
else {
NSLog(#" %#", [error userInfo]);
}
}
// Update the array and table view.
[currentRows removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:YES];
}
}
currentRows is a NSArray of the objects you display in the table.
Cheers

Save object in CoreData

I am using CoreData with iPhone SDK. I am making a notes app. I have a table with note objects displayed from my model. When a button is pressed I want to save the text in the textview to the object being edited. How do I do this? I've been trying several things but none seem to work.
Thanks
EDIT:
NSManagedObjectContext *context = [fetchedResultsController managedObjectContext];
NSEntityDescription *entity = [[fetchedResultsController fetchRequest] entity];
NSManagedObject *newManagedObject = [NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:context];
[newManagedObject setValue:detailViewController.textView.text forKey:#"noteText"];
NSError *error;
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();
}
The above code saves it correctly but it saves it as a new object. I want it to be saved as the one I have selected in my tableView.
You should check out the Core Data Programming Guide. It's hard to know exactly what you want from the question, but the basic idea is:
-(IBAction)saveNote { //hooked up in Interface Builder (or programmatically)
self.currentNote.text = self.textField.text; //assuming currentNote is an NSManagedObject subclass with a property called text, and textField is the UITextField
}
//later, at a convenient time such as application quit
NSError *error = nil;
[self.managedObjectContext save:&error]; //saves the context to disk
EDIT: If you want to edit a preexisting object, you should get the object from the fetched results controller, e.g. NSManagedObject *currentObject = [fetchedResultsController objectAtIndexPath:[self.tableView indexPathForSelectedRow]], then edit that object. I'd also recommend using a custom subclass of NSManagedObject with property declarations, rather than using setValue:forKey, since it's more flexible.