Обработка ошибок в основных данных iPhone - PullRequest
83 голосов
/ 14 февраля 2010

Я видел в примере кода, предоставленном 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();
}

Но никогда не бывает примеров того, как вы должны реализовать это.

Кто-нибудь имеет (или может указать мне направление) какой-то действительный "производственный" код, иллюстрирующий вышеуказанный метод.

Спасибо заранее, Matt

Ответы [ 5 ]

32 голосов
/ 15 февраля 2010

Никто не собирается показывать вам рабочий код, потому что он на 100% зависит от вашего приложения и места возникновения ошибки.

Лично я поместил там утверждение assert, потому что в 99,9% случаев эта ошибка будет возникать в процессе разработки, и когда вы исправите ее там, очень вряд ли вы увидите ее в работе.

После подтверждения я хотел бы представить пользователю предупреждение, сообщить ему, что произошла неисправимая ошибка и что приложение будет закрыто. Вы также можете поместить там объявление с просьбой связаться с разработчиком, чтобы можно было надеяться, что это будет сделано.

После этого я бы оставил там функцию abort (), так как она "вылетит" из приложения и сгенерирует трассировку стека, которую можно надеяться использовать позже для отслеживания проблемы.

31 голосов
/ 18 августа 2010

Это один общий метод, который я придумал для обработки и отображения ошибок проверки на 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];
        }
    }
}

Наслаждайтесь.

6 голосов
/ 24 июня 2017

Я удивлен, что никто здесь фактически не обрабатывает ошибку так, как она должна быть обработана. Если вы посмотрите на документацию, вы увидите.

Типичные причины ошибки: * Устройство вышло из строя. пространства. * Постоянный магазин недоступен из-за разрешения или защита данных, когда устройство заблокировано. * The Не удалось перенести хранилище в текущую версию модели. * The родительский каталог не существует, не может быть создан или запрещен письменная форма.

Поэтому, если я обнаружил ошибку при настройке основного стека данных, я меняю rootViewController в UIWindow и показываю пользовательский интерфейс, который четко сообщает пользователю, что его устройство может быть заполнено, или его настройки безопасности слишком высоки для работы этого приложения. , Я также даю им кнопку «Повторить попытку», чтобы они могли попытаться исправить проблему до повторной попытки стека данных ядра.

Например, пользователь может освободить место для хранения, вернуться в мое приложение и нажать кнопку повторной попытки.

Утверждает? В самом деле? Слишком много разработчиков в комнате!

Меня также удивляет количество онлайн-уроков, в которых не упоминается, как операция сохранения может завершиться неудачей по этим причинам. Поэтому вам нужно будет убедиться, что любое событие сохранения В ЛЮБОМ МЕСТЕ в вашем приложении может произойти сбой, потому что устройство ПРОСТО В ЭТОЙ МИНУТЕ заполнилось вашими приложениями, сохраняющими сохранение-сохранение.

5 голосов
/ 03 сентября 2014

Я нашел эту общую функцию сохранения гораздо лучшим решением:

- (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 Chris

2 голосов
/ 25 августа 2016

Я сделал 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"
}`
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...