NSFetchResultsController Shows False Objects

I have a UITableView showing objects from a Core Data object received from WebServer through NSFetchResultController. The user can change them and send them back to the server. She can also click a button to update these objects from the server.

Each object has an identifier attribute. When I get a JSON object from the server, I look for an existing object with the same identifier. It exists, I update it. Otherwise, I create it.

Some of these events happen to the main queue NSManagedObjectContext, some of them in the Private Queue child queue. In all cases, this happens in the method performBlock, and both the child context and its parent are saved.

It sounds like a sample template with bread and butter. Now my problem is:

Sometimes, after updating the server, it NSFetchResultControllershows two instances of the same object. Two copies are different (their pointers are different). One copy is complete, the other has its own attribute values, not its relationship. Both have the same NSManagedObjectContext. Both have the same identifier.

How can I debug such a problem? I checked that there are no two instances of the same object in my CoreData repository (by searching inside the SQLite file, and also by placing a character breakpoint on awakeFromInsert). I drew code that looks for an existing instance, and it finds this in order.

I'm stuck at this point, and it's hard for me to imagine a debugging strategy.

, , , , , .

.

JD

1: controller:didChangeObject:atIndexPath:forChangeType:newIndexPath:

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath
{
    UITableView *tableView = self.tableView;
    switch(type) {
        case NSFetchedResultsChangeInsert:
            [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;

        case NSFetchedResultsChangeDelete:
            [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;

        case NSFetchedResultsChangeUpdate:
            [self configureCell:(DaySlotCell*)[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
            break;

        case NSFetchedResultsChangeMove:
            [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
            [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]withRowAnimation:UITableViewRowAnimationFade];
            break;
    }
}

2: . singleton, ( , SGIServer). :

  • mainManagedObjectContext, a NSMainQueueConcurrencyType, UI , NSFetchResultController ( , NSFetchResultController ), . :

  • persistentManagedObjectContext, a NSPrivateQueueConcurrencyType, , :

:

NSManagedObjectContext *managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
managedObjectContext.persistentStoreCoordinator = persistentStoreCoordinator;
self.persistentManagedObjectContext = managedObjectContext;
managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
managedObjectContext.parentContext = self.persistentManagedObjectContext;
self.mainManagedObjectContext = managedObjectContext;

, , , :

NSManagedObjectContext *moc = [server mainManagedObjectContext];

NSManagedObjectContext *moc = [server newPrivateContext];

newPrivateContext NSPrivateQueueConcurrencyType , :

- (NSManagedObjectContext *) newPrivateContext
{
    NSManagedObjectContext *privateContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
    privateContext.parentContext = self.mainManagedObjectContext;
    return privateContext;
}

, save, :

- (void)syncSaveContext: (NSManagedObjectContext *) moc persisting:(BOOL)saveToDisk
{
    NSManagedObjectContext *mainContext = self.mainManagedObjectContext;

    if (moc && moc != mainContext) {
        NSError *error = nil;
        if (![moc save:&error]) {
            NSLog(@"Error saving MOC: %@\n%@",[error localizedDescription], [error userInfo]);
        }
    }

    if (mainContext && [mainContext hasChanges]) {
        [mainContext performBlockAndWait:^{
            NSError *error = nil;
            if (![mainContext save:&error]) {
                NSLog(@"Error saving MOC: %@\n%@",[error localizedDescription], [error userInfo]);
            }
        }];
    }
    if (saveToDisk) {
        NSManagedObjectContext *privateContext = self.persistentManagedObjectContext;

        if (privateContext && [privateContext hasChanges]) {
            [privateContext performBlockAndWait: ^{
                NSError *error = nil;
                if (![privateContext save:&error]) {
                    NSLog(@"Error saving private MOC: %@\n%@",[error localizedDescription], [error userInfo]);
                }
            }];
        }
    }
}

- (void)asyncSaveContext: (NSManagedObjectContext *) moc persisting:(BOOL)saveToDisk
{
    NSManagedObjectContext *mainContext = self.mainManagedObjectContext;

    if (moc && moc != mainContext) {
        NSError *error = nil;
        if (![moc save:&error]) {
            NSLog(@"Error saving MOC: %@\n%@",[error localizedDescription], [error userInfo]);
        }
    }

    if (mainContext && [mainContext hasChanges]) {
        [mainContext performBlock:^{
            NSError *error = nil;
            if ([mainContext save:&error]) {
                if (saveToDisk) {
                    NSManagedObjectContext *privateContext = self.persistentManagedObjectContext;

                    if (privateContext && [privateContext hasChanges]) {
                        [privateContext performBlock: ^{
                            NSError *error = nil;
                            if (![privateContext save:&error]) {
                                NSLog(@"Error saving private MOC: %@\n%@",[error localizedDescription], [error userInfo]);
                            }
                        }];
                    }
                }
            } else {
                NSLog(@"Error saving MOC: %@\n%@",[error localizedDescription], [error userInfo]);
            }
        }];
    }
}

- , , . , , , .

+4
1

- ( , ), , , , , , . ,

save, obtainPermanentIDsForObjects , :

[self.parentContext performBlockAndWait:^{
            NSError * error = nil;
            [self.parentContext obtainPermanentIDsForObjects:[self.parentContext.insertedObjects allObjects] error:&error];
            [self.parentContext save: &error]
        }];
+3

Source: https://habr.com/ru/post/1613421/


All Articles