Search code examples
objective-ccore-datansfetchedresultscontroller

`NSFetchedResultsController` fetched data changes with and without save when using predicate


NSFetchedResultsCotroller behaves strangely when using predicate. The following code prints the output:

Rows - 0

But when I comment out [self saveContext] (the first one) the output becomes:

Rows - 1

Removing the predicate (query.predicate) fixes things and the output is always:

Rows - 1

I suspect it has something to do with how NULL is compared in memory vs in DB query. Any idea what is actually going on?

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    NSError *error;

    // Deletes persistent store coordinator behind the scene

    // Create an empty entity with the optional fields attr1 (string) and attr2 (date)
    Entity *e = [NSEntityDescription insertNewObjectForEntityForName:@"Entity" inManagedObjectContext:[self managedObjectContext]];
    [self saveContext]; // <---------------- Code to comment out

    // Setup fetched results controller
    NSPredicate *pred = [NSPredicate predicateWithFormat:@"(attr1 != %@) AND (attr2 != %@)", @"", [NSDate new], nil];

    NSFetchRequest *query = [[NSFetchRequest alloc] initWithEntityName:@"Entity"];
    query.predicate = pred;
    query.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"attr1" ascending:NO]];

    frc = [[NSFetchedResultsController alloc] initWithFetchRequest:query managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:nil];
    frc.delegate = self;
    [frc performFetch:&error];

    // Output #1
    NSLog(@"Rows - %ld", frc.fetchedObjects.count);

    [self saveContext];

    return YES;
}

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
    NSLog(@"Rows - %ld (Update)", controller.fetchedObjects.count);
}

Edit:

Modifying the predicate seems to fix it. Still no idea why:

    NSPredicate *pred = [NSPredicate predicateWithFormat:@"((attr1 = NULL) OR (attr1 != %@)) AND ((attr2 = NULL) OR (attr2 != %@))", @"", date];

Solution

  • Yes, I would suspect it has to do with your predicate. I think the behavior of nil as the substitution object is unpredictable. You can either use NULL or nil in the format string itself or a real object that represents null, i.e. [NSNull null]. If you try this, maybe your original predicate will also work.

    But there is something else going on. Perhaps your entity has some default values that make the predicate fail, which could explain why the save could change the fetch result under certain circumstances. Also, it is not clear what the content of the saveContext method is.

    Also, not that [NSDate new] produces a date that is accurate to 1/10000th of a second, so this part of the predicate should always return true.