shohaku
shohaku

Reputation: 349

How to Efficiently Reset Attributes in a Core Data Entity

My app uses Core Data and has an attribute called 'beenSeen'. When a user refreshes the app, all 'beenSeen' values of 1 are changed to 0. On an iPod Touch 2nd gen with over 2000 objects, refreshing takes over a minute. My code looks like this:

    for (Deck *deck in self.deckArray) {
        if ([deck.beenSeen isEqualToNumber:[NSNumber numberWithInt:1]]) {
            [deck setBeenSeen:[NSNumber numberWithInt:0]];
            [self.managedObjectContext save:&error];           
        }
    }

I'm also considering deleting the sqlite file and having an alert ask the user to restart the app themselves. Doing that sure is a whole lot quicker than what I have now. Is there a quicker way to refresh an entity? Could I have a 'backup' entity and copy it over? Thanks for any help.

Upvotes: 0

Views: 305

Answers (2)

Scott Forbes
Scott Forbes

Reputation: 7417

Hm. The first optimization I'd suggest would be

for (Deck *deck in self.deckArray) {
    if ([deck.beenSeen isEqualToNumber:[NSNumber numberWithInt:1]]) {
        [deck setBeenSeen:[NSNumber numberWithInt:0]];
    }
}
[self.managedObjectContext save:&error];           

I suspect it might speed things up to do one big context save, instead of 2,000 little ones.

The second suggestion would be to try getting rid of the if test – if the majority of your beenSeen values are changing from 1 to 0, and the others are already 0, then you might as well just set all of them to 0 and save the time of checking each one individually. (On the other hand, if there are 10,000 objects and you're resetting 2,000 of them, then getting rid of the test might not be optimal.)

for (Deck *deck in self.deckArray) {
    [deck setBeenSeen:[NSNumber numberWithInt:0]];
    }
    [self.managedObjectContext save:&error];           
}

The third suggestion would be to think about implementing this another way – for instance, your deck object could implement a lastSeen attribute, storing the date and time when the deck was last seen, and then instead of doing a mass reset (and writing 2,000 Core Data rows) you could just test each deck's lastSeen date and time against the timestamp of the last user refresh.

Upvotes: 1

D33pN16h7
D33pN16h7

Reputation: 2030

Try this, First, filter the array using a predicate:

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"beenSeen == %@", 
                          [NSNumber numberWithInt:1]];

NSArray* filtered = [self.deckArray filteredArrayUsingPredicate:predicate];

Now set the new value:

[filtered setValue:[NSNumber numberWithInt:0] forKeyPath:@"beenSeen"];

Finally save the context:

[self.managedObjectContext save:&error];

Hope this helps :)

Upvotes: 1

Related Questions