我有一位客户就我的应用程序中的错误与我联系,我认为这与我使用Core Data有关。这只是一个理论,因为我自己无法重新创建这个问题。
问题似乎是如果用户创建一个新的实体对象然后立即编辑实体对象它似乎没有正确持久,并且下次用户打开应用程序时该对象不存在。
如果用户创建实体对象并退出应用程序而不进行编辑,则不会出现问题,如果用户在重新打开应用程序后编辑对象,则也不会发生。
我只有一个关于这种情况的报告,有问题的设备是运行IOS4.0.2的iPhone 3G。我不能在iPhone 3GS,iPhone 4或模拟器上重新创建它(全部运行4.0.2)
我不是核心数据专家,我想知道我使用框架的方式是否存在问题。如果有人会审查我下面的代码,如果他们发现任何潜在的问题,我会非常感激。
我已经包含了与相关类的核心数据交互的方法。
的AppDelegate
- (void)applicationDidEnterBackground:(UIApplication *)application {
NSError *error = nil;
if (managedObjectContext_ != nil) {
if ([managedObjectContext_ hasChanges] && ![managedObjectContext_ save:&error]) {
// log
}
}
}
- (void)applicationWillTerminate:(UIApplication *)application {
NSError *error = nil;
if (managedObjectContext_ != nil) {
if ([managedObjectContext_ hasChanges] && ![managedObjectContext_ save:&error]) {
// log
}
}
}
- (NSManagedObjectContext *)managedObjectContext {
if (managedObjectContext_ != nil) {
return managedObjectContext_;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
managedObjectContext_ = [[NSManagedObjectContext alloc] init];
[managedObjectContext_ setPersistentStoreCoordinator:coordinator];
}
return managedObjectContext_;
}
- (NSManagedObjectModel *)managedObjectModel {
if (managedObjectModel_ != nil) {
return managedObjectModel_;
}
managedObjectModel_ = [[NSManagedObjectModel mergedModelFromBundles:nil] retain];
return managedObjectModel_;
}
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (persistentStoreCoordinator_ != nil) {
return persistentStoreCoordinator_;
}
NSURL *storeURL = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory] stringByAppendingPathComponent: @"mydatabase.sqlite"]];
NSError *error = nil;
persistentStoreCoordinator_ = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if (![persistentStoreCoordinator_ addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
NSLog(@"An error occurred adding the persistentStoreCoordinator. %@, %@", error, [error userInfo]);
// Display an alert message if an error occurs
}
return persistentStoreCoordinator_;
}
控制器1(使用通过图像选择器选择的图像创建实体对象)
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
UIImage* selectedImage = [info objectForKey:UIImagePickerControllerOriginalImage];
// Create an image object for the new image.
NSManagedObject *image = [NSEntityDescription insertNewObjectForEntityForName:@"Image" inManagedObjectContext:managedObjectContext];
// Set the image for the image managed object.
[image setValue:[selectedImage scaleAndCropImageToScreenSize] forKey:@"image"];
// Create a thumbnail version of the image.
UIImage *imageThumbnail = [selectedImage thumbnailImage:50.0F];
// Create a new note
Note *note = [NSEntityDescription insertNewObjectForEntityForName:@"Note" inManagedObjectContext:managedObjectContext];
NSDate *now = [[NSDate alloc] init];
note.createdDate = now;
note.lastModifiedDate = now;
[now release];
note.thumbnail = imageThumbnail;
note.image = image;
}
[self dismissModalViewControllerAnimated:YES];
[picker release];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NotePadViewController *notePadController = [[NotePadViewController alloc] initWithNibName:@"NotePadView" bundle:nil];
Note *note = [[self fetchedResultsController] objectAtIndexPath:indexPath];
notePadController.note = note;
notePadController.title = note.notes;
[self.navigationController pushViewController:notePadController animated:YES];
[notePadController release];
}
- (NSFetchedResultsController *)fetchedResultsController {
if (fetchedResultsController != nil) {
return fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Note" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
[fetchRequest setFetchBatchSize:20];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"createdDate" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:nil cacheName:@"Root"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
[aFetchedResultsController release];
[fetchRequest release];
[sortDescriptor release];
[sortDescriptors release];
NSError *error = nil;
if (![fetchedResultsController performFetch:&error]) {
NSLog(@"An error occured retrieving fetched results. %@, %@", error, [error userInfo]);
// Display an alert message if an error occurs
}
return fetchedResultsController;
}
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
[self.tableView beginUpdates];
}
- (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:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
break;
case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView reloadSections:[NSIndexSet indexSetWithIndex:newIndexPath.section] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {
switch(type) {
case NSFetchedResultsChangeInsert:
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
[self setControllerTitle];
[self.tableView endUpdates];
}
控制器2 - 用于查看和编辑实体对象(添加文本)
- (void)saveAction:(id)sender {
NSDate *now = [[NSDate alloc] init];
if (self.note != nil && textView.text.length == 0) {
self.note.notes = nil;
self.note.lastModifiedDate = now;
} else if (textView.text.length != 0) {
// Create a new note if one does not exist
if (self.note == nil) {
VisualNotesAppDelegate *appDelegate = (VisualNotesAppDelegate *)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *managedObjectContext = appDelegate.managedObjectContext;
Note *newNote = [NSEntityDescription insertNewObjectForEntityForName:@"Note" inManagedObjectContext:managedObjectContext];
self.note = newNote;
self.note.createdDate = now;
}
self.note.lastModifiedDate = now;
self.note.notes = textView.text;
self.title = [note title];
}
[now release];
[self.textView resignFirstResponder];
}
非常感谢提前。
答案 0 :(得分:2)
首先,你真的不应该忽视这些错误。你可能会在那里抛出一个错误而永远不会知道它。
没有什么能真正跳出代码本身,但你可能很容易在保存过程中出现错误,可能是非可选参数未设置等等。
处理异常并从那里看。
答案 1 :(得分:1)
问题是我只在应用程序终止或后台时保存托管对象上下文。
在应用程序退出之前,我只有大约5秒的时间用于保存用户创建的所有图像blob。如果有很多图像,那么它们可能不会全部保存,这就是重新启动应用程序时不显示的原因。
这主要影响iPhone 3G,因为它是我支持的所有型号中最慢的。
我已经更新了我的代码,以便在进行更改时保存托管上下文。这解决了这个问题,但暴露了另一个问题:
答案 2 :(得分:0)
如果实体仅在saveAction:
方法中创建,那么如果用户在手动选择保存之前退出应用程序,则可能无法首先创建实体。