iphone Core Data Erro não resolvido ao salvar

169

Estou recebendo uma mensagem de erro estranha dos dados principais ao tentar salvar, mas o problema de que o erro não é reproduzível (aparece em momentos diferentes ao executar tarefas diferentes)

a mensagem de erro:

Unresolved error Domain=NSCocoaErrorDomain Code=1560 UserInfo=0x14f5480 "Operation could not be completed. (Cocoa error 1560.)", {
NSDetailedErrors = (
Error Domain=NSCocoaErrorDomain Code=1570 UserInfo=0x5406d70 "Operation could not be completed. (Cocoa error 1570.)",
Error Domain=NSCocoaErrorDomain Code=1570 UserInfo=0x14f9be0 "Operation could not be completed. (Cocoa error 1570.)"
);
}

e o método que gera o erro é:

- (IBAction)saveAction:(id)sender {
    NSError *error;
    if (![[self managedObjectContext] save:&error]) {
        // Handle error
        NSLog(@"Unresolved error %@, %@, %@", error, [error userInfo],[error localizedDescription]);
        exit(-1);  // Fail
    }
}

alguma idéia para o motivo desta mensagem? dando que apareça em momentos aleatórios

Ahmed Kotb
fonte
Isso pode ajudá-lo: "Tratamento de erros de" produção "dos dados principais do iPhone" stackoverflow.com/questions/2262704/…
Johannes Fahrenkrug 13/14

Respostas:

296

Isso significa que uma propriedade obrigatória foi atribuída a zero. No seu modelo * .xcodatam, marque a caixa "opcional" ou, quando estiver salvando no managedObjectContext, verifique se as propriedades estão preenchidas.

Se você estiver recebendo mais erros após alterar seu código para atender aos dois requisitos, tente limpar sua compilação e exclua o aplicativo do seu iPhone Simulator / iPhone. A alteração do seu modelo pode entrar em conflito com a implementação do modelo antigo.

Editar:

Eu quase esqueci aqui todos os códigos de erro que o Core Data cospe: Referência de constantes de dados principais Eu já tive problemas com isso antes e percebi que desmarquei a caixa opcional correta. Tanta dificuldade para descobrir o problema. Boa sorte.

David Wong
fonte
2
Isso resolveu para mim. Observe também que, pelo menos na minha experiência, mesmo que não tenha sido salva no arquivo sqlite, as alterações chegaram ao contexto. Portanto, o comportamento pode ser irregular quando isso acontece.
nickthedude 27/02
Não consegui encontrar a causa raiz, mas consegui resolver o problema, tornando todas as propriedades opcionais.
Michael Osofsky 31/08
Você tentou o código de Charles, ele informava qual campo é o problema.
David Wong
233

Eu lutei com isso por um tempo. O verdadeiro problema aqui é que a depuração que você tem não está mostrando qual é o problema. A razão para isso é porque CoreData colocará uma matriz de objetos NSError no objeto NSError de "nível superior" que retornará se houver mais de um problema (é por isso que você vê o erro 1560, que indica vários problemas, e uma matriz de erros 1570s). Parece que o CoreData possui um punhado de chaves usadas para esconder informações no erro retornado se houver um problema que fornecerá informações mais úteis (como a entidade na qual o erro ocorreu, o relacionamento / atributo que estava faltando etc.) ) As chaves usadas para inspecionar o dicionário userInfo podem ser encontradas nos documentos de referência aqui .

Este é o bloco de código que eu uso para obter uma saída razoável do erro retornado durante um salvamento:

    NSError* error;
    if(![[survey managedObjectContext] save:&error]) {
        NSLog(@"Failed to save to data store: %@", [error localizedDescription]);
        NSArray* detailedErrors = [[error userInfo] objectForKey:NSDetailedErrorsKey];
        if(detailedErrors != nil && [detailedErrors count] > 0) {
            for(NSError* detailedError in detailedErrors) {
                NSLog(@"  DetailedError: %@", [detailedError userInfo]);
            }
        }
        else {
            NSLog(@"  %@", [error userInfo]);
        }
    }

Ele produzirá uma saída que informa os campos ausentes, o que facilita a solução do problema.

Charles
fonte
Muito obrigado por este código. Isso torna o rastreamento de problemas do CoreData muito mais simples.
31511 MiKL
21

Estou jogando isso como uma resposta, mesmo que seja realmente mais um enfeite para o trecho de Charles. A saída direta do NSLog pode ser uma bagunça para ler e interpretar, então eu gosto de inserir algum espaço em branco e chamar o valor de algumas teclas críticas 'userInfo'.

Aqui está uma versão do método que eu tenho usado. ('_sharedManagedObjectContext' é um #define para '[[[UIApplication sharedApplication] delegado] managedObjectContext]'.)

- (BOOL)saveData {
    NSError *error;
    if (![_sharedManagedObjectContext save:&error]) {
        // If Cocoa generated the error...
        if ([[error domain] isEqualToString:@"NSCocoaErrorDomain"]) {
            // ...check whether there's an NSDetailedErrors array            
            NSDictionary *userInfo = [error userInfo];
            if ([userInfo valueForKey:@"NSDetailedErrors"] != nil) {
                // ...and loop through the array, if so.
                NSArray *errors = [userInfo valueForKey:@"NSDetailedErrors"];
                for (NSError *anError in errors) {

                    NSDictionary *subUserInfo = [anError userInfo];
                    subUserInfo = [anError userInfo];
                    // Granted, this indents the NSValidation keys rather a lot
                    // ...but it's a small loss to keep the code more readable.
                    NSLog(@"Core Data Save Error\n\n \
                      NSValidationErrorKey\n%@\n\n \
                      NSValidationErrorPredicate\n%@\n\n \
                      NSValidationErrorObject\n%@\n\n \
                      NSLocalizedDescription\n%@", 
                      [subUserInfo valueForKey:@"NSValidationErrorKey"], 
                      [subUserInfo valueForKey:@"NSValidationErrorPredicate"], 
                      [subUserInfo valueForKey:@"NSValidationErrorObject"], 
                      [subUserInfo valueForKey:@"NSLocalizedDescription"]);
                }
            }
            // If there was no NSDetailedErrors array, print values directly
            // from the top-level userInfo object. (Hint: all of these keys
            // will have null values when you've got multiple errors sitting
            // behind the NSDetailedErrors key.
            else {
                    NSLog(@"Core Data Save Error\n\n \
                      NSValidationErrorKey\n%@\n\n \
                      NSValidationErrorPredicate\n%@\n\n \
                      NSValidationErrorObject\n%@\n\n \
                      NSLocalizedDescription\n%@", 
                      [userInfo valueForKey:@"NSValidationErrorKey"], 
                      [userInfo valueForKey:@"NSValidationErrorPredicate"], 
                      [userInfo valueForKey:@"NSValidationErrorObject"], 
                      [userInfo valueForKey:@"NSLocalizedDescription"]);

            }
        } 
        // Handle mine--or 3rd party-generated--errors
        else {
            NSLog(@"Custom Error: %@", [error localizedDescription]);
        }
        return NO;
    }
    return YES;
}

Isso me permite ver o valor de 'NSValidationErrorKey', que, quando encontrei o problema do OP, apontou diretamente para as entidades não-opcionais do Core Data que eu esqueci de definir antes de tentar salvar.

clozach
fonte
Também é muito útil. Especialmente quando você obtém essas strings de descrição de entidade de dados principais \ n \ n \ n.
Lukasz
Arrumado. 'message' não é utilizado.
pojo 28/02
0

O problema me comoveu quando salvou o segundo registro no CoreData. Todos os campos não opcionais (relacionamento) também foram preenchidos sem nulo, mas na saída de erro eu observaria que um dos campos no primeiro objeto salvo se tornara nulo. Estranho um pouco? Mas o motivo é bastante trivial - um para um relacionamento que anula o primeiro objeto, quando o defino no segundo.

Então, o esquema é:

"Parent" with relationship "child" One to One
Create Child 1, set parent. Save - OK
Create Child 2, set parent. Save - Error, Child 1.Parent == nil
(behind the scene child 2 did nullify child 1 parent)

Alterar o relacionamento no pai de um para um para muitos para um resolveu esta tarefa.

HotJard
fonte
0

Eu tinha uma propriedade transitória do tipo int que não era opcional. Obviamente, quando foi definido como 0, o erro 1570 aparece. Acabei de alterar todas as minhas propriedades transitórias para opcional. A lógica de verificação nula pode ser implementada em código, se necessário.

Anton Plebanovich
fonte
0

Quero dizer que seu modelo falhou na validação, o que pode acontecer por vários motivos: propriedade não utilizada em seu modelo, valor ausente marcado como necessário. Para entender melhor o que exatamente deu errado, coloque um ponto de interrupção em um local onde você esteja pronto para salvar seu objeto e chame uma das validateFor...variantes do método, como:

po [myObject validateForInsert]

Informações mais detalhadas sobre o problema estão na descrição do erro. A validação bem sucedida significa que você não obterá saída.

kkodev
fonte
0

Isso me ajudou. Verifique este também.

Marque a caixa opcional em seus objetos * .xcodatamodel

ssowri1
fonte