iPhone Core Data Error no resuelto al guardar

169

Recibo un mensaje de error extraño de los datos principales al intentar guardar, pero el problema es que el error no es reproducible (aparece en diferentes momentos cuando se realizan diferentes tareas)

el mensaje de error:

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.)"
);
}

y el método que genera el error es:

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

alguna idea por la razón de este mensaje? dado que aparece en momentos aleatorios

Ahmed Kotb
fuente
Esto podría ayudarlo: "Manejo de errores de" producción "de datos principales de iPhone" stackoverflow.com/questions/2262704/…
Johannes Fahrenkrug

Respuestas:

296

Significa que hay una propiedad obligatoria asignada nula. Ya sea en su * .xcodatamodel marque la casilla "opcional" o cuando esté guardando en managedObjectContext asegúrese de que sus propiedades estén completas.

Si obtiene más errores después de cambiar su código para cumplir con los dos requisitos, intente limpiar su compilación y elimine la aplicación de su iPhone Simulator / dispositivo iPhone. Su cambio de modelo puede entrar en conflicto con la implementación del modelo anterior.

Editar:

Casi me olvido de que aquí están todos los códigos de error que escupe Core Data: Referencia de constantes de datos centrales Tuve problemas con esto antes y me di cuenta de que desmarqué la casilla opcional correcta. Tantos problemas para descubrir el problema. Buena suerte.

David Wong
fuente
2
Esto lo resolvió para mí. También tenga en cuenta que, al menos en mi experiencia, aunque no se guardó en el archivo sqlite, los cambios se abrieron paso en el contexto. Entonces el comportamiento puede ser errático cuando esto sucede.
nickthedude
No pude llegar a la causa raíz, pero logré solucionar el problema haciendo que todas las propiedades fueran opcionales.
Michael Osofsky
¿Probaste el código de Charles? Te diría qué campo es el problema.
David Wong
233

Luché con esto por un tiempo yo mismo. El verdadero problema aquí es que la depuración que tienes no te muestra cuál es el problema. La razón de esto es porque CoreData colocará una matriz de objetos NSError en el objeto NSError de "nivel superior" que devuelve si hay más de un problema (es por eso que ve el error 1560, que indica múltiples problemas, y una matriz de error 1570). Parece que CoreData tiene un puñado de claves que utiliza para esconder información en el error que devuelve si hay un problema que le dará más información útil (como la entidad en la que ocurrió el error, la relación / atributo que faltaba, etc. ) Las claves que utiliza para inspeccionar el diccionario userInfo se pueden encontrar en los documentos de referencia aquí .

Este es el bloque de código que uso para obtener un resultado razonable del error devuelto durante un guardado:

    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]);
        }
    }

Producirá una salida que le indicará los campos que faltan, lo que hace que solucionar el problema sea mucho más fácil de tratar.

Charles
fuente
Muchas gracias por este código. Hace que el seguimiento de problemas de CoreData sea mucho más simple.
MiKL
21

Estoy lanzando esto como respuesta, aunque en realidad es más un adorno para el fragmento de Charles. La salida directa de NSLog puede ser un desastre para leer e interpretar, por lo que me gusta agregar un poco de espacio en blanco y llamar el valor de algunas claves críticas de 'userInfo'.

Aquí hay una versión del método que he estado usando. ('_sharedManagedObjectContext' es un #define para '[[[UIApplication sharedApplication] delegate] 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;
}

Esto me permite ver el valor de 'NSValidationErrorKey', que, cuando encontré el problema desde el OP, apuntó directamente a las entidades de datos centrales no opcionales que había olvidado configurar antes de intentar guardar.

clozach
fuente
También muy útil. Especialmente cuando obtienes estas cadenas de descripción de entidad de datos de núcleo \ n \ n \ n sin procesar.
Lukasz
Ordenado. 'mensaje' no se utiliza por cierto.
pojo
0

El problema me conmovió cuando guardé el segundo registro en CoreData. Todos los campos no opcionales (relación) también se llenaron sin nulo, pero en la salida de error me di cuenta de que uno de los campos en el primer objeto guardado se convirtió en nulo. Extraño un poco? Pero la razón es bastante trivial: una relación uno a uno que anula el primer objeto, cuando lo configuro en el segundo.

Entonces, el esquema es:

"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)

Cambiar la relación en Parent de One to One a Many to One resolvió esta tarea.

HotJard
fuente
0

Tenía una propiedad transitoria de tipo int que no era opcional. Obviamente, cuando se estableció en 0, aparece el error 1570. Acabo de cambiar todas mis propiedades transitorias a opcional. La lógica de verificación nula se puede implementar en el código si es necesario.

Anton Plebanovich
fuente
0

Quiero decir que su modelo no se pudo validar, lo que podría suceder por varias razones: propiedad no utilizada en su modelo, valor faltante que está marcado como requerido. Para comprender mejor qué salió mal exactamente, coloque un punto de interrupción en un lugar donde esté listo para guardar su objeto y llame a una de las validateFor...variantes del método, como:

po [myObject validateForInsert]

Información más detallada sobre el problema se encuentra en la descripción del error. La validación exitosa significa que no obtendrá resultados.

kkodev
fuente
0

Me ayudó. Mira este también.

Marque la casilla opcional en sus objetos * .xcodatamodel

ssowri1
fuente