Question

I still have difficulties to understand how core data works in background thread even after having read lots of things about it, especially for deleting objects.

As an example, if I want to delete an object from a context like this :

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSManagedObjectContext *context = [self managedObjectContext];
    if (editingStyle == UITableViewCellEditingStyleDelete)
    {
        // Delete object from database
        [context deleteObject:[self.tests objectAtIndex:indexPath.row]];
        NSError *error = nil;
        if (![context save:&error]) {
            NSLog(@"Can't Delete! %@ %@", error, [error localizedDescription]);
            return;
        }
        [self.tests removeObjectAtIndex:indexPath.row];
        [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
    }
}

This works, but when the data is big, [context save:&error] takes a lot of time, so how can I do it in background ? It seems that I can't work with another context, otherwise I get the error an nsmanagedobjectcontext cannot delete objects in other contexts. I have tried hundreds of different things, but I'm lost... Thanks !

Was it helpful?

Solution 3

Take a look at the performBlock and performBlockAndWait methods on NSManagedObjectContext.

I have been favoring using NSPrivateQueueConcurrencyType even for my UI bound managed object contexts because it avoids exactly this situation. For example you could do something like this:

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSManagedObjectContext *context = [self managedObjectContext];
    if (editingStyle == UITableViewCellEditingStyleDelete)
    {
        NSManagedObject *m = [self.tests objectAtIndex:indexPath.row];
        [context performBlock:^{
            [context deleteObject:m];

            if (![context save:&error]) {
                //Note: You should really do something more useful than log this
                NSLog(@"Can't Delete! %@ %@", error, [error localizedDescription]);
            }
        }];
        [self.tests removeObjectAtIndex:indexPath.row];
        [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
    }
}

The performBlock call is asynchronous (as opposed to its synchronous counterpart performBlockAndWait) which will allow the tableView update to proceed as the context is busy saving on a separate thread.

As an aside, your managed object context instance should not change very often. It is common for small applications to only allocate a single instance of NSManagedObjectContxt during their lifecycle.

OTHER TIPS

You can only delete object in a context; in which you fetched that managed object. If you are using seprate thread with new context, in that case you should get the objectId from managedObject you got from the first context so you can delete object

[context objectWithID:objectId]];

The NSManagedObjectID is the same between contexts, but the NSManagedObject itself is not.

As the other answer mentioned, you can only delete an object from its context and a context is thread bound. The issue here is not how long the save takes but where you are saving.

You should avoid expensive operations in any UI facing method call like this one. There is no reason to save immediately after a delete. Save later, save when the user is expecting a delay in the UI. Core Data will work just fine without the save.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top