Nsundomanager desfazer não trabalhar com dados principais
-
21-09-2019 - |
Pergunta
Estou tentando criar um aplicativo para iPhone onde o usuário possa adicionar entradas. Quando ele pressionar uma nova entrada, uma caixa aparecerá pedindo algumas informações. Em seguida, ele pode pressionar "Cancelar" ou "salvar" para descartar os dados ou salvá -los no disco.
Para salvar, estou usando a estrutura de dados principal, que funciona muito bem. No entanto, não consigo fazer o botão "Cancelar" funcionar. Quando a janela aparece, solicitando informações, crio um novo objeto no contexto do objeto gerenciado (MOC). Então, quando o usuário pressiona cancelar, tento usar o nsundomanager pertencente ao MOC.
Eu também gostaria de fazer isso usando grupos de desfazer aninhados, porque pode haver grupos aninhados.
Para testar isso, escrevi um aplicativo simples. O aplicativo é apenas o modelo "Aplicativo baseado em janela" com dados principais ativados. Para o modelo de dados principal, crio uma única entidade chamada "entidade" com atributo inteiro "x". Então, dentro do ApplicationDidfinishlaunching, adiciono este código:
- (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];
}
A ideia é simples. Tente inserir um novo objeto de entidade, desfazer -o, buscar todos os objetos de entidade no MOC e imprimi -los. Se tudo funcionou corretamente, não deve haver objetos no final.
No entanto, recebo esta saída:
[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
Como você pode ver, o objeto está presente no MOC depois de tentar desfazer sua criação. Alguma sugestão sobre o que estou fazendo de errado?
Solução
Seu problema é causado pelo fato de que, diferentemente do OS X, o contexto de objeto gerenciado pelo iPhone não contém um gerente de desfazer por padrão. Você precisa adicionar explicitamente um.
Altere o código gerado no delegado do aplicativo para a propriedade ManagedObjectContext para se parecer com a seguinte:
- (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;
}
Depois de fazer essa alteração, a segunda mensagem de log não está mais impressa.
Espero que ajude...
Dave
Outras dicas
Eu tentei a abordagem de Dave, mas não funcionei para mim. Finalmente encontrei a solução no exemplo da Apple Coredatabooks
O truque é criar um novo contexto que compartilha o coordenador com o contexto do aplicativo. Para descartar as alterações que você não precisa fazer nada, basta descartar o novo objeto de contexto. Desde que você compartilha o coordenador, salvar atualiza seu contexto principal.
Aqui está minha versão adaptada, onde eu uso um objeto estático para o contexto temporário para criar um novo objeto 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;
}
Espero que ajude
Gonso
Deve funcionar. Você atribuiu corretamente o gerenciador de desfazer ao seu gerenciadoBjectContext? Se você fez isso com razão, por padrão, desfazer o registro ativado e você deve estar pronto. Há um bom artigo sobre dados principais aqui. Há um bom tutorial sobre dados principais e nsundomanager aqui. Espero que ajude.