He visto en el código de ejemplo proporcionado por Apple referencias sobre cómo debe manejar los errores de Core Data. Es decir:
NSError *error = nil;
if (![context save:&error]) {
/*
Replace this implementation with code to handle the error appropriately.
abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.
*/
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
Pero nunca ejemplos de cómo debe implementarlo.
¿Alguien tiene (o me puede señalar en la dirección de) algún código de "producción" real que ilustre el método anterior?
Gracias de antemano, Matt
iphone
core-data
error-handling
Influencia
fuente
fuente
Respuestas:
Nadie te va a mostrar el código de producción porque depende al 100% de tu aplicación y de dónde ocurre el error.
Personalmente, puse una declaración de aserción allí porque el 99,9% de las veces este error ocurrirá en el desarrollo y cuando lo arregle allí es muy poco probable que lo vea en producción.
Después de la afirmación, presentaría una alerta al usuario, le informaría que ocurrió un error irrecuperable y que la aplicación se cerrará. También puede poner una propaganda allí pidiéndoles que se comuniquen con el desarrollador para que, con suerte, pueda rastrear esto.
Después de eso, dejaría el abort () allí, ya que "bloqueará" la aplicación y generará un seguimiento de pila que, con suerte, podrá usar más adelante para rastrear el problema.
fuente
-save:
llamada de datos centrales . Todas esas condiciones ocurren mucho antes de que su código llegue a este punto.-save:
se llame.Este es un método genérico que se me ocurrió para manejar y mostrar errores de validación en el iPhone. Pero Marcus tiene razón: probablemente querrá modificar los mensajes para que sean más fáciles de usar. Pero esto al menos le da un punto de partida para ver qué campo no se validó y por qué.
- (void)displayValidationError:(NSError *)anError { if (anError && [[anError domain] isEqualToString:@"NSCocoaErrorDomain"]) { NSArray *errors = nil; // multiple errors? if ([anError code] == NSValidationMultipleErrorsError) { errors = [[anError userInfo] objectForKey:NSDetailedErrorsKey]; } else { errors = [NSArray arrayWithObject:anError]; } if (errors && [errors count] > 0) { NSString *messages = @"Reason(s):\n"; for (NSError * error in errors) { NSString *entityName = [[[[error userInfo] objectForKey:@"NSValidationErrorObject"] entity] name]; NSString *attributeName = [[error userInfo] objectForKey:@"NSValidationErrorKey"]; NSString *msg; switch ([error code]) { case NSManagedObjectValidationError: msg = @"Generic validation error."; break; case NSValidationMissingMandatoryPropertyError: msg = [NSString stringWithFormat:@"The attribute '%@' mustn't be empty.", attributeName]; break; case NSValidationRelationshipLacksMinimumCountError: msg = [NSString stringWithFormat:@"The relationship '%@' doesn't have enough entries.", attributeName]; break; case NSValidationRelationshipExceedsMaximumCountError: msg = [NSString stringWithFormat:@"The relationship '%@' has too many entries.", attributeName]; break; case NSValidationRelationshipDeniedDeleteError: msg = [NSString stringWithFormat:@"To delete, the relationship '%@' must be empty.", attributeName]; break; case NSValidationNumberTooLargeError: msg = [NSString stringWithFormat:@"The number of the attribute '%@' is too large.", attributeName]; break; case NSValidationNumberTooSmallError: msg = [NSString stringWithFormat:@"The number of the attribute '%@' is too small.", attributeName]; break; case NSValidationDateTooLateError: msg = [NSString stringWithFormat:@"The date of the attribute '%@' is too late.", attributeName]; break; case NSValidationDateTooSoonError: msg = [NSString stringWithFormat:@"The date of the attribute '%@' is too soon.", attributeName]; break; case NSValidationInvalidDateError: msg = [NSString stringWithFormat:@"The date of the attribute '%@' is invalid.", attributeName]; break; case NSValidationStringTooLongError: msg = [NSString stringWithFormat:@"The text of the attribute '%@' is too long.", attributeName]; break; case NSValidationStringTooShortError: msg = [NSString stringWithFormat:@"The text of the attribute '%@' is too short.", attributeName]; break; case NSValidationStringPatternMatchingError: msg = [NSString stringWithFormat:@"The text of the attribute '%@' doesn't match the required pattern.", attributeName]; break; default: msg = [NSString stringWithFormat:@"Unknown error (code %i).", [error code]]; break; } messages = [messages stringByAppendingFormat:@"%@%@%@\n", (entityName?:@""),(entityName?@": ":@""),msg]; } UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Validation Error" message:messages delegate:nil cancelButtonTitle:nil otherButtonTitles:@"OK", nil]; [alert show]; [alert release]; } } }
Disfrutar.
fuente
Me sorprende que nadie aquí esté manejando el error de la forma en que debe manejarse. Si miras la documentación, verás.
Entonces, si encuentro un error al configurar la pila de datos central, cambio el rootViewController de UIWindow y muestro una interfaz de usuario que le dice claramente al usuario que su dispositivo podría estar lleno o que sus configuraciones de seguridad son demasiado altas para que esta aplicación funcione. También les doy un botón "intentar de nuevo", para que puedan intentar solucionar el problema antes de que se vuelva a intentar la pila de datos centrales.
Por ejemplo, el usuario podría liberar espacio de almacenamiento, regresar a mi aplicación y presionar el botón intentar nuevamente.
Afirma? De Verdad? ¡Demasiados desarrolladores en la sala!
También me sorprende la cantidad de tutoriales en línea que no mencionan cómo una operación de guardado podría fallar también por estas razones. Por lo tanto, deberá asegurarse de que cualquier evento guardado en CUALQUIER LUGAR de su aplicación pueda fallar porque el dispositivo SOLO ESTE MINUTO se llenó con sus aplicaciones guardando guardado guardado.
fuente
Encontré esta función de guardado común una solución mucho mejor:
- (BOOL)saveContext { NSError *error; if (![self.managedObjectContext save:&error]) { DDLogError(@"[%@::%@] Whoops, couldn't save managed object context due to errors. Rolling back. Error: %@\n\n", NSStringFromClass([self class]), NSStringFromSelector(_cmd), error); [self.managedObjectContext rollback]; return NO; } return YES; }
Siempre que un guardado falle, esto revertirá su NSManagedObjectContext, lo que significa que restablecerá todos los cambios que se han realizado en el contexto desde el último guardado . Por lo tanto, debe tener cuidado para mantener siempre los cambios utilizando la función de guardado anterior lo antes posible y con regularidad, ya que de lo contrario podría perder datos fácilmente.
Para insertar datos, esta podría ser una variante más flexible que permita que otros cambios continúen:
- (BOOL)saveContext { NSError *error; if (![self.managedObjectContext save:&error]) { DDLogError(@"[%@::%@] Whoops, couldn't save. Removing erroneous object from context. Error: %@", NSStringFromClass([self class]), NSStringFromSelector(_cmd), object.objectId, error); [self.managedObjectContext deleteObject:object]; return NO; } return YES; }
Nota: Estoy usando CocoaLumberjack para iniciar sesión aquí.
¡Cualquier comentario sobre cómo mejorar esto es más que bienvenido!
BR Chris
fuente
Hice una versión rápida de la útil respuesta de @JohannesFahrenkrug que puede ser útil:
public func displayValidationError(anError:NSError?) -> String { if anError != nil && anError!.domain.compare("NSCocoaErrorDomain") == .OrderedSame { var messages:String = "Reason(s):\n" var errors = [AnyObject]() if (anError!.code == NSValidationMultipleErrorsError) { errors = anError!.userInfo[NSDetailedErrorsKey] as! [AnyObject] } else { errors = [AnyObject]() errors.append(anError!) } if (errors.count > 0) { for error in errors { if (error as? NSError)!.userInfo.keys.contains("conflictList") { messages = messages.stringByAppendingString("Generic merge conflict. see details : \(error)") } else { let entityName = "\(((error as? NSError)!.userInfo["NSValidationErrorObject"] as! NSManagedObject).entity.name)" let attributeName = "\((error as? NSError)!.userInfo["NSValidationErrorKey"])" var msg = "" switch (error.code) { case NSManagedObjectValidationError: msg = "Generic validation error."; break; case NSValidationMissingMandatoryPropertyError: msg = String(format:"The attribute '%@' mustn't be empty.", attributeName) break; case NSValidationRelationshipLacksMinimumCountError: msg = String(format:"The relationship '%@' doesn't have enough entries.", attributeName) break; case NSValidationRelationshipExceedsMaximumCountError: msg = String(format:"The relationship '%@' has too many entries.", attributeName) break; case NSValidationRelationshipDeniedDeleteError: msg = String(format:"To delete, the relationship '%@' must be empty.", attributeName) break; case NSValidationNumberTooLargeError: msg = String(format:"The number of the attribute '%@' is too large.", attributeName) break; case NSValidationNumberTooSmallError: msg = String(format:"The number of the attribute '%@' is too small.", attributeName) break; case NSValidationDateTooLateError: msg = String(format:"The date of the attribute '%@' is too late.", attributeName) break; case NSValidationDateTooSoonError: msg = String(format:"The date of the attribute '%@' is too soon.", attributeName) break; case NSValidationInvalidDateError: msg = String(format:"The date of the attribute '%@' is invalid.", attributeName) break; case NSValidationStringTooLongError: msg = String(format:"The text of the attribute '%@' is too long.", attributeName) break; case NSValidationStringTooShortError: msg = String(format:"The text of the attribute '%@' is too short.", attributeName) break; case NSValidationStringPatternMatchingError: msg = String(format:"The text of the attribute '%@' doesn't match the required pattern.", attributeName) break; default: msg = String(format:"Unknown error (code %i).", error.code) as String break; } messages = messages.stringByAppendingString("\(entityName).\(attributeName):\(msg)\n") } } } return messages } return "no error" }`
fuente