IPhone Core Data "Производство" Обработка ошибок

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

Ответ 1

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

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

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

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

Ответ 2

Это один общий метод, с которым я столкнулся, чтобы обрабатывать и отображать ошибки проверки на 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?:@""),([email protected]": ":@""),msg];
            }
            UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Validation Error" 
                                                            message:messages
                                                           delegate:nil 
                                                  cancelButtonTitle:nil otherButtonTitles:@"OK", nil];
            [alert show];
            [alert release];
        }
    }
}

Enjoy.

Ответ 3

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

- (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, что означает, что reset все изменения, которые были выполнены в контексте с момента последнего сохранения. Поэтому вы должны внимательно следить за тем, чтобы всегда сохранять изменения с помощью вышеуказанной функции сохранения как можно раньше и регулярно, так как вы можете легко потерять данные в противном случае.

Для вставки данных это может быть более слабый вариант, позволяющий другим изменениям жить:

- (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 Крис

Ответ 4

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

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

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

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

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

Я также удивлен количеством обучающих онлайн, которые не упоминают, как операция сохранения может потерпеть неудачу по этим причинам. Таким образом, вам нужно будет гарантировать, что любое событие сохранения в любом приложении может потерпеть неудачу, потому что устройство JUST THIS MINUTE заполнилось с сохранением экономии ваших приложений.

Ответ 5

Я сделал 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"
}`