objective-ccore-datansoperationqueuensblockoperation

Strange Core-Data behaviour


I perform some work using an NSBlockOperation and inside this block the persistent store loses its persistent stores.

the following code:

- (void) eraseCoreData_ManagedObjectsInArray:(NSArray *) arrayOfManagedObjectsToDelete usingManagedContext:(NSManagedObjectContext *) managedObjectContext
{
    NSLog(@"Managed object context is %@", managedObjectContext);
    NSLog(@"----->Persistent store of the deletion context has %d stores", [managedObjectContext.persistentStoreCoordinator.persistentStores count]);

    // add the operation to the queue with a block
    [self.coreDataDeletionQueue addOperationWithBlock:^{

    for (NSManagedObject *objectToDelete in arrayOfManagedObjectsToDelete) {
        [managedObjectContext deleteObject:objectToDelete];
    }

    NSLog(@"Managed object context in operation block is %@", managedObjectContext);
    NSLog(@"Persistence store coordinator in operation block is %@", managedObjectContext.persistentStoreCoordinator);
    NSLog(@"Persistent store of the deletion context in operation block has %d stores", [managedObjectContext.persistentStoreCoordinator.persistentStores count]);

    NSError *saveError = nil;
    if (![managedObjectContext save:&saveError]) {
        NSLog(@"Couldn't save Core-Data state for the deleted objects result: %@", [saveError localizedDescription]);
    }
    }];
}

logs this out:

Managed object context is < NSManagedObjectContext: 0xa46ff10 >

----->Persistent store of the deletion context has 1 stores

Managed object context in operation block is < NSManagedObjectContext: 0xa46ff10 >

Persistence store coordinator in operation block is < NSPersistentStoreCoordinator: 0xa1a45e0 >

Persistent store of the deletion context in operation block has 0 stores

* Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'This NSPersistentStoreCoordinator has no persistent stores. It cannot perform a save operation.'

I am not sure why this sudden lose of stores, could it be due to the fact that this is running in a test suite that I set to use an in memory store and the memory is not shared with this NSOperationQueue that executes the blocks?


Solution

  • My guess you are using your code in the wrong manner.

    First, each thread must have its own context. So, inside the block operation you need to create a new context and use it within that block.

    NSManagedObjectContext *blockContext = [[NSManagedObjectContext alloc] init];
    [blockContext setPersistentStoreCoordinator:[managedObjectContext persistentStoreCoordinator]];
    

    Then, if you need to share objects within contexts, use NSManagedObjectID and not NSManagedObject. Before creating that block save the main context or you will have only temporary ids. Otherwise use - (BOOL)obtainPermanentIDsForObjects:(NSArray *)objects error:(NSError **)error to obtain permanent ids without saving.

    NSArray *objectIDs = [arrayOfManagedObjectsToDelete valueForKey:@"objectID"];
    

    Within the block use - (NSManagedObject *)existingObjectWithID:(NSManagedObjectID *)objectID error:(NSError **)error to retrieve the object you are interested in through blockContext.

    Your code could look like

    NSArray *objectIDs = [arrayOfManagedObjectsToDelete valueForKey:@"objectID"];
    
    // add the operation to the queue with a block
    [self.coreDataDeletionQueue addOperationWithBlock:^{
    
        NSManagedObjectContext *blockContext = [[NSManagedObjectContext alloc] init];
        [blockContext setPersistentStoreCoordinator:[managedObjectContext persistentStoreCoordinator]];
    
        NSError* error = nil;
        for (NSManagedObject *objectID in objectIDs) {
            // some error handling here...
            NSManagedObject* objectToDelete = [existingObjectWithID:objectID error:&error];
            [blockContext deleteObject:objectToDelete];
        }
    
        if (![blockContext save:&error]) {
            NSLog(@"Couldn't save Core-Data state for the deleted objects result: %@", [error localizedDescription]);
        }
    }];