Garry Pettet
Garry Pettet

Reputation: 8288

Altering ManagedObjects In NSArray

I have an entity called 'Job' with two boolean attributes named 'completed' and 'logged'. I am trying to retrieve all completed jobs that have not been logged at app start-up and change them to logged. I'm able to get all the completed but unlogged jobs with this fetchRequest:

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(completed == %@ && logged == %@)", [NSNumber numberWithBool:YES], [NSNumber numberWithBool:NO]];

I'm then assigning this predicate to a fetchRequest and calling the [managedObjectContext executeFetchRequest:fetchRequest] method to get an array of all Job entities that meet this criteria. This seems to work fine and is returning the correct number of jobs.

What I've been trying to do is loop through the NSArray returned, set the logged attribute to YES and then save. This seems to complete and doesn't return any errors but the changes are not persisted when the application quits. Where am I going wrong?

[fetchRequest setPredicate:predicate];
NSError error;
NSArray jobsToLog = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
if ([jobsToLog count] > 0) {
   for (int i = 0; i < [jobsToLog count] - 1; i++) {
      [[jobsToLog objectAtIndex:i] setLogged:[NSNumber numberWithBool:YES]];
      // Commit the changes made to disk
      error = nil;
      if (![managedObjectContext save:&error]) {
         // An error occurred
      }
   }
}

Thanks in anticipation,

Upvotes: 0

Views: 336

Answers (4)

Marcus S. Zarra
Marcus S. Zarra

Reputation: 46718

First, you can clean things up a bit via:

[fetchRequest setPredicate:predicate];
NSError *error = nil;
NSArray jobsToLog = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
NSAssert1(error == nil, @"Error retrieving jobs: %@", [error userInfo]);
for (id job in jobsToLog) {
  [job setValue:[NSNumber numberWithBool:YES] forKey:@"logged"];
}
if (![managedObjectContext save:&error]) {
  NSAssert1(NO, @"Failed to save %@", [error userInfo]);
}
  • Note that I start with error being nil and being a pointer (your declaration was not). If you do not then the initial state of that pointer is undefined and definitely not nil.
  • Check to make sure there is no error in the fetch.
  • The check against count is unnecessary as the fast enumerator will handle it for you.
  • Saving after every object is wasteful, unless you have thousands of objects, save at the end.
  • Walk through your code in the debugger and make sure you are getting objects back and are looping over them.

Upvotes: 1

MrHen
MrHen

Reputation: 2480

Some notes that may help:

The for loop is looping from 0 to one less than the count. This will skip the last job. If there is only 1 job, nothing will happen. Change the loop to:

for (int i = 0; i < [jobsToLog count]; i++)

or

for (int i = 0; i <= [jobsToLog count] - 1; i++)

When pulling objects out of NSArray the compiler will not know its type. You should explicitly cast the object:

  [(Job *)[jobsToLog objectAtIndex:i] setLogged:[NSNumber numberWithBool:YES]];

You can do both of the above using fast enumeration:

for(Job *thisJob in jobsToLog) {
  [thisJob setLogged:[NSNumber numberWithBool:YES];
}

Upvotes: 1

regulus6633
regulus6633

Reputation: 19030

I'm no expert here but I think you need to tell the managed object context by using setPrimitiveValue: to make the changes. So try this.

[self willChangeValueForKey:@"logged"];
[self setPrimitiveValue:[NSNumber numberWithBool:YES] forKey:@"logged"];
[self didChangeValueForKey:@"logged"];

Upvotes: 0

diederikh
diederikh

Reputation: 25281

What if you use the KVC (Key Value Coding) method:

[[jobsToLog objectAtIndex:i] setValue:[NSNumber numberWithBool:YES] forKey:@"logged"];

Do you have a setLogged method in your custom NSManagedObject class?

Upvotes: 0

Related Questions