Я видел в примере кода, предоставленном Apple, ссылки на то, как следует обрабатывать ошибки Core Data. Т.е.:
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();
}
Но ни разу никаких примеров того, как это следует реализовать.
Есть ли у кого-нибудь (или может указать мне направление) некий реальный «производственный» код, который иллюстрирует вышеуказанный метод.
Заранее спасибо, Мэтт
Ответы:
Никто не собирается показывать вам производственный код, потому что он на 100% зависит от вашего приложения и места возникновения ошибки.
Лично я ставлю заявление Assert там , потому что 99,9% времени эта ошибка будет возникать в развитии и когда вы фиксируете его там весьма маловероятно , что вы будете видеть его в производство.
После утверждения я бы представил пользователю предупреждение, чтобы он знал, что произошла неисправимая ошибка и что приложение собирается выйти. Вы также можете разместить там рекламное объявление с просьбой связаться с разработчиком, чтобы вы могли отслеживать, как это было сделано.
После этого я бы оставил abort () там, так как он "вылетит" приложение и сгенерирует трассировку стека, которую вы, надеюсь, сможете использовать позже, чтобы отследить проблему.
источник
-save:
вызов основных данных . Все эти условия возникают задолго до того, как ваш код достигнет этой точки.-save:
вызова.Это один из общих методов, которые я придумал для обработки и отображения ошибок проверки на iPhone. Но Маркус прав: вы, вероятно, захотите настроить сообщения, чтобы они были более удобными для пользователя. Но это, по крайней мере, дает вам отправную точку, чтобы увидеть, какое поле не было проверено и почему.
- (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]; } } }
Наслаждаться.
источник
Я удивлен, что здесь никто не обрабатывает ошибку так, как задумано. Если вы посмотрите документацию, то увидите.
Поэтому, если я обнаруживаю ошибку при настройке основного стека данных, я заменяю rootViewController UIWindow и показываю пользовательский интерфейс, который четко сообщает пользователю, что его устройство может быть заполнено или его настройки безопасности слишком высоки для работы этого приложения. Я также даю им кнопку «Повторить попытку», чтобы они могли попытаться исправить проблему до того, как будет выполнена повторная попытка основного стека данных.
Например, пользователь может освободить место для хранения, вернуться в мое приложение и нажать кнопку «Повторить попытку».
Утверждает? В самом деле? Слишком много разработчиков в комнате!
Я также удивлен количеством онлайн-руководств, в которых не упоминается, как операция сохранения может завершиться неудачно по этим причинам. Таким образом, вам нужно будет убедиться, что любое событие сохранения в ЛЮБОМ месте в вашем приложении может завершиться ошибкой, потому что устройство ТОЛЬКО В ЭТУ МИНУТУ было заполнено вашими приложениями, сохраняющими сохранение.
источник
Я нашел эту общую функцию сохранения гораздо лучшим решением:
- (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; }
Всякий раз, когда сохранение не удается, это откатит ваш NSManagedObjectContext, что означает сброс всех изменений, которые были выполнены в контексте с момента последнего сохранения . Таким образом, вы должны внимательно следить за тем, чтобы всегда сохранять изменения, используя указанную выше функцию сохранения, как можно раньше и регулярно, поскольку в противном случае вы можете легко потерять данные.
Для вставки данных это может быть более свободный вариант, позволяющий продолжать другие изменения:
- (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; }
Примечание: я использую CocoaLumberjack для регистрации здесь.
Любые комментарии о том, как это улучшить, более чем приветствуются!
BR Крис
источник
Я сделал Swift-версию полезного ответа @JohannesFahrenkrug, который может быть полезен:
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" }`
источник