Я пытаюсь создать приложение для iPhone, где пользователь может добавить записи. Когда он нажмет новую запись, поле откроет выяснение у него для некоторой информации. Затем он может или нажать "Cancel" или "Save", чтобы отбросить данные или сохранить его на диск.
Для сохранения я использую Базовую платформу Данных, которая работает вполне прилично. Однако я не могу заставить кнопку "Cancel" работать. Когда окно открывается, запрашивая информацию, я создаю новый объект в контексте управляемого объекта (MOC). Затем, когда пользователь нажимает отмену, я пытаюсь использовать NSUndoManager, принадлежащий MOC.
Я также хотел бы сделать это с помощью вложенных групп отмены, потому что могли бы быть вложенные группы.
Для тестирования этого я записал простое приложение. Приложение является просто шаблоном "Window based application" с Базовыми включенными Данными. Для Базовой Модели данных я создаю единственный объект под названием "Объект" с целочисленным атрибутом "x". Затем в applicationDidFinishLaunching, я добавляю этот код:
- (void)applicationDidFinishLaunching:(UIApplication *)application {
// Override point for customization after app launch
unsigned int x=arc4random()%1000;
[self.managedObjectContext processPendingChanges];
[self.managedObjectContext.undoManager beginUndoGrouping];
NSManagedObject *entity=[NSEntityDescription insertNewObjectForEntityForName:@"Entity"
inManagedObjectContext:self.managedObjectContext];
[entity setValue:[NSNumber numberWithInt:x] forKey:@"x"];
NSLog(@"Insert Value %d",x);
[self.managedObjectContext processPendingChanges];
[self.managedObjectContext.undoManager endUndoGrouping];
[self.managedObjectContext.undoManager undoNestedGroup];
NSFetchRequest *fetchRequest=[[NSFetchRequest alloc] init];
NSEntityDescription *entityEntity=[NSEntityDescription entityForName:@"Entity"
inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entityEntity];
NSArray *result=[self.managedObjectContext executeFetchRequest:fetchRequest error:nil];
for(entity in result) {
NSLog(@"FETCHED ENTITY %d",[[entity valueForKey:@"x"] intValue]);
}
[window makeKeyAndVisible];
}
Идея проста. Попытайтесь вставить новый объект Объекта, отменить его, выбрать все объекты Объекта в MOC и распечатать их. Если бы все работало правильно, то не должно быть никаких объектов в конце.
Однако я получаю этот вывод:
[Session started at 2010-02-20 13:41:49 -0800.]
2010-02-20 13:41:51.695 Untitledundotes[7373:20b] Insert Value 136
2010-02-20 13:41:51.715 Untitledundotes[7373:20b] FETCHED ENTITY 136
Как Вы видите, объект присутствует в MOC после того, как я пытаюсь отменить его создание. Какие-либо предложения относительно того, что я делаю неправильно?
Ваша проблема вызвана тем, что, в отличие от OS X, управляемый объект iPhone контекст не содержит диспетчера отмены по умолчанию. Вам необходимо явно добавить его.
Измените сгенерированный код в делегате приложения для свойства managedObjectContext, чтобы он выглядел следующим образом:
- (NSManagedObjectContext *) managedObjectContext {
if (managedObjectContext != nil) {
return managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
managedObjectContext = [[NSManagedObjectContext alloc] init];
//add the following 3 lines of code
NSUndoManager *undoManager = [[NSUndoManager alloc] init];
[managedObjectContext setUndoManager:undoManager];
[undoManager release];
[managedObjectContext setPersistentStoreCoordinator: coordinator];
}
return managedObjectContext;
}
После внесения этого изменения второе сообщение журнала будет больше не печатается.
Надеюсь, это поможет ...
Дэйв
Я попробовал подход Дейва, но у меня ничего не вышло. Я наконец нашел решение в примере Apple CoreDataBooks
Уловка состоит в том, чтобы создать новый контекст, который разделяет координатора с вашим приложением. контекст. Чтобы отменить изменения, вам не нужно ничего делать, просто отбросьте новый объект контекста. Поскольку вы разделяете координатора, при сохранении обновляется ваш основной контекст.
Вот моя адаптированная версия, в которой я использую статический объект для временного контекста для создания нового объекта ChannelMO.
//Gets a new ChannelMO that is part of the addingManagedContext
+(ChannelMO*) getNewChannelMO{
// Create a new managed object context for the new channel -- set its persistent store coordinator to the same as that from the fetched results controller's context.
NSManagedObjectContext *addingContext = [[NSManagedObjectContext alloc] init];
addingManagedObjectContext = addingContext;
[addingManagedObjectContext setPersistentStoreCoordinator:[[self getContext] persistentStoreCoordinator]];
ChannelMO* aux = (ChannelMO *)[NSEntityDescription insertNewObjectForEntityForName:@"ChannelMO" inManagedObjectContext:addingManagedObjectContext];
return aux;
}
+(void) saveAddingContext{
NSNotificationCenter *dnc = [NSNotificationCenter defaultCenter];
[dnc addObserver:self selector:@selector(addControllerContextDidSave:)
name:NSManagedObjectContextDidSaveNotification object:addingManagedObjectContext];
NSError *error;
if (![addingManagedObjectContext save:&error]) {
// Update to handle the error appropriately.
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
exit(-1); // Fail
}
[dnc removeObserver:self name:NSManagedObjectContextDidSaveNotification object:addingManagedObjectContext];
// Release the adding managed object context.
addingManagedObjectContext = nil;
}
Надеюсь, это поможет
Гонсо