In prepping my app for an update, I've found a weird issue that's so far been a bit of a head-scratcher.
I have a simple method that fetches a managed object, updates one attribute and then saves the change to the persistent store. The weird thing is that some of the time, it doesn't seem to actually save all the way down the stack to the DB and yet it returns true for a successful save and doesn't populate the NSError object.
I've verified this by turning on SQL logging - in one call, I see no UPDATE statement but in a second call of the same method with the same inputs, I see the UPDATE.
Really weird. I must be doing something wrong but I've been staring at this all day and I can't figure it out.
Here's the method in question:
+ (void)markTemplateAsPurchasedWithProductID:(NSString *)productID inContext:(NSManagedObjectContext *)context {
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"TripTemplate" inManagedObjectContext:context];
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:@"(productID = %@)", productID]];
[fetchRequest setEntity:entity];
NSError *error;
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
[fetchRequest release];
if ([fetchedObjects count] > 0) {
TripTemplate *template = [fetchedObjects lastObject];
template.purchased = [NSNumber numberWithBool:YES];
NSLog(@"Marking '%@' as Purchased: %@", template.name, template.purchased);
NSError *saveError;
if (![context save:&saveError])
NSLog(@"Error Saving Purchased For Template: %@ - %@", template, saveError);
} else {
...
//log fetch error
}
}
Here are the two sets of logs I see when calling this method.
I've verified that in both cases they're being called from the main thread.
They're being run one after the other.
Run #1 (no SQL update):
2011-04-30 17:25:27.107 App[15024:707] CoreData: sql: SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZLASTAPPSTOREPRICE, t0.ZPURCHASED, t0.ZISFREE, t0.ZNAME, t0.ZPRODUCTID, t0.ZSERVERID, t0.ZTRIPDESCRIPTION, t0.ZAUTHORDESCRIPTION, t0.ZAUTHORURL, t0.ZCREATEDAT, t0.ZAUTHORNAME FROM ZTRIPTEMPLATE t0 WHERE t0.ZPRODUCTID = ?
2011-04-30 17:25:27.110 App[15024:707] CoreData: annotation: sql connection fetch time: 0.0028s
2011-04-30 17:25:27.111 App[15024:707] CoreData: annotation: total fetch execution time: 0.0043s for 1 rows.
2011-04-30 17:25:27.112 App[15024:707] Marking 'Steve's Creations' as Purchased: 1
Run #2 (SQL update):
2011-04-30 17:27:37.536 App[15024:707] CoreData: sql: SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZLASTAPPSTOREPRICE, t0.ZPURCHASED, t0.ZISFREE, t0.ZNAME, t0.ZPRODUCTID, t0.ZSERVERID, t0.ZTRIPDESCRIPTION, t0.ZAUTHORDESCRIPTION, t0.ZAUTHORURL, t0.ZCREATEDAT, t0.ZAUTHORNAME FROM ZTR开发者_StackOverflowIPTEMPLATE t0 WHERE t0.ZPRODUCTID = ?
2011-04-30 17:27:37.537 App[15024:707] CoreData: annotation: sql connection fetch time: 0.0015s
2011-04-30 17:27:37.538 App[15024:707] CoreData: annotation: total fetch execution time: 0.0024s for 1 rows.
2011-04-30 17:27:37.539 App[15024:707] Marking 'Steve's Creations' as Purchased: 1
2011-04-30 17:27:37.540 App[15024:707] CoreData: sql: BEGIN EXCLUSIVE
2011-04-30 17:27:37.542 App[15024:707] CoreData: sql: UPDATE ZTRIPTEMPLATE SET ZPURCHASED = ?, Z_OPT = ? WHERE Z_PK = ? AND Z_OPT = ?
2011-04-30 17:27:37.544 App[15024:707] CoreData: sql: COMMIT
Any suggestions where I might start looking for my problem?
In both cases, the fetch works so it seems the MOC is at least capable of talking to the store.
In looking at the code, the only thing I could suggest as an error would be that you are somehow passing different context to the method. If so, and you don't merge the contexts, then changes won't show if you save in one but examine from another.
I would suggest logging the entire template object, the context and the context's updatedObjects. You need to make sure you are talking to the objects you think you are.
精彩评论