Проверка управляемого объекта

Существует два типа проверки — уровень свойства и межсвойство. Вы используете проверку уровня свойства для обеспечения правильности отдельных значений; Вы используете проверку межсвойства для обеспечения правильности комбинаций значений.

Базовое подтверждение правильности данных

Какао обеспечивает основную инфраструктуру для проверки значения модели. Это требует, чтобы Вы, тем не менее, записали код для всех ограничений, которые Вы хотите применить. Базовые Данные позволяют Вам помещать логику проверки в модель управляемого объекта. Можно указать максимальные и минимальные значения для атрибутов даты и числового; максимальные и минимальные длины для строковых атрибутов и регулярное выражение, которое должен соответствовать строковый атрибут. Можно также указать ограничения на отношения, например что они обязательны или не могут превысить определенное число. Можно поэтому указать наиболее распространенные ограничения на значения атрибута, не пишущий кода.

Если Вы действительно хотите настроить проверку отдельных свойств, Вы используете стандартные методы проверки, как определено NSKeyValueCoding протокол и описал в Проверке Уровня свойства). Базовые Данные также расширяют проверку до проверки отношений и межзначений свойств. Они описаны в проверке Межсвойства.

Важно понять, что то, как проверить, является решением модели, когда проверить, пользовательский интерфейс или решение уровня контроллера (например, привязка значения для текстового поля могла бы иметь его, “сразу проверяет” включенную опцию). Кроме того, неоднократно, несоответствия, как ожидают, возникнут в управляемых объектах и графах объектов.

Нет ничего для запрещения объекта в памяти от становления противоречивым на временной основе. Ограничения проверки применяются Базовыми Данными только во время работы «сохранения» или по запросу (можно вызвать методы проверки непосредственно как и когда Вы желаете). Иногда может быть полезно проверить изменения, как только они сделаны и сразу сообщить об ошибках. Это может предотвратить пользователя, подаренного длинный список ошибок, когда они наконец прибывают для сохранения их работы. Если управляемые объекты потребовались, чтобы быть всегда в допустимом состоянии, оно было бы среди других вещей вызывать определенный поток операций на конечном пользователе. Это также подкрепляет идею контекста управляемого объекта, представляющего «временную память» — в целом можно принести управляемые объекты на временную память и отредактировать их, однако, Вы желаете прежде в конечном счете или фиксация изменений или отбрасывание их.

Проверка уровня свойства

NSKeyValueCoding протокол указывает метод —validateValue:forKey:error:— это предоставляет общую поддержку для методов проверки похожим способом к этому в который valueForKey: предоставляет поддержку для методов доступа.

Если Вы хотите реализовать логику в дополнение к ограничениям, Вы обеспечиваете в модели управляемого объекта, Вы не должны переопределять validateValue:forKey:error:. Вместо этого необходимо реализовать методы формы validate<Key>:error:.

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

-(BOOL)validateAge:(id *)ioValue error:(NSError **)outError {
 
    if (*ioValue == nil) {
        // trap this in setNilValueForKey? new NSNumber with value 0?
        return YES;
    }
    if ([*ioValue floatValue] <= 0.0) {
        if (outError != NULL) {
        NSString *errorStr = NSLocalizedStringFromTable(
            @"Age must greater than zero", @"Employee",
            @"validation: zero age error");
        NSDictionary *userInfoDict = @{ NSLocalizedDescriptionKey : errorStr };
        NSError *error = [[NSError alloc] initWithDomain:EMPLOYEE_ERROR_DOMAIN
            code:PERSON_INVALID_AGE_CODE
            userInfo:userInfoDict];
        *outError = error;
        }
        return NO;
    }
    else {
        return YES;
    }
    // . . .

Входное значение является указателем на ссылку на объект ( id *). Это означает, что в принципе можно изменить входное значение. Выполнению так, однако, строго обескураживают, поскольку существуют потенциально серьезные проблемы с управлением памятью (см. Проверку Значения ключа в Значении ключа, Кодирующем Руководство по программированию). Кроме того, Вы не должны вызывать validateValue:forKey:error: в пользовательских методах проверки свойства. Если Вы сделаете, то Вы создадите бесконечный цикл когда validateValue:forKey:error: вызывается во время выполнения.

Если Вы изменяете входное значение в a validate<Key>:error: метод, необходимо гарантировать, чтобы Вы только изменили значение, если это недопустимо или добровольно. Причина состоит в том, что, так как объект и контекст теперь dirtied, Базовые Данные могут проверить тот ключ снова позже. Если Вы продолжаете выполнять приведение в методе проверки, это может поэтому произвести бесконечный цикл. Точно так же необходимо также быть осторожными при реализации проверки и willSave методы, производящие мутации или побочные эффекты — Базовые Данные, подтвердят те изменения, пока не будет достигнуто устойчивое состояние.

Проверка межсвойства

Это возможно для значений всех отдельных атрибутов объекта быть допустимым и все же для комбинации значений, чтобы быть недопустимым. Рассмотрите, например, заявление, хранящее информацию о людях включая их возраст и есть ли у них водительские права. Для объекта Лица, 12 могло бы быть допустимое значение для age атрибут, и YES допустимое значение для a hasDrivingLicense атрибут, но (в большинстве стран, по крайней мере) эта комбинация значений была бы недопустима.

NSManagedObject предоставляет дополнительные возможности для проверки — обновление, вставку, и удаление — через validateFor… методы такой как validateForUpdate:. При реализации пользовательских методов проверки межсвойства Вы вызываете реализацию суперкласса сначала, чтобы гарантировать, что также вызываются отдельные методы проверки свойства. Если реализация суперкласса перестала работать (т.е. если существует недопустимое значение атрибута), то Вы можете:

  1. Возвратиться NO и ошибка создается реализацией суперкласса.

  2. Продолжайте выполнять проверку, ища противоречивые комбинации значений.

Если Вы продолжаете, необходимо удостовериться, что любые значения, которые Вы используете в своей логике, не самостоятельно недопустимы таким способом, которым Ваш код мог бы самостоятельно вызвать ошибки (например, если существует атрибут, значение которого требуется, чтобы, больше, чем 0, который является фактически 0 так приводит проверку к сбою, но который Вы используете в качестве делителя в вычислении). Кроме того, при обнаружении дальнейших ошибок проверки необходимо объединить их с существующей ошибкой и возвратить “многократную ошибочную ошибку”, как описано по Объединяющимся Ошибкам Проверки.

Следующий пример показывает реализацию метода проверки межсвойства для объекта Лица, имеющего два атрибута, birthday и hasDrivingLicense. Ограничение состоит в том, что у лица в возрасте меньше чем 16 лет не может быть водительских прав. Это ограничение проверяется в обоих validateForInsert: и validateForUpdate:, таким образом, сама логика проверки является factored в отдельный метод.

  Проверка Межсвойства перечисления 1 для объекта Лица

- (BOOL)validateForInsert:(NSError **)error
{
    BOOL propertiesValid = [super validateForInsert:error];
    // could stop here if invalid
    BOOL consistencyValid = [self validateConsistency:error];
    return (propertiesValid && consistencyValid);
}
 
- (BOOL)validateForUpdate:(NSError **)error
{
    BOOL propertiesValid = [super validateForUpdate:error];
    // could stop here if invalid
    BOOL consistencyValid = [self validateConsistency:error];
    return (propertiesValid && consistencyValid);
}
 
 
- (BOOL)validateConsistency:(NSError **)error
{
    static     NSCalendar *gregorianCalendar;
 
    BOOL valid = YES;
    NSDate *myBirthday = [self birthday];
 
    if ((myBirthday != nil) && ([[self hasDrivingLicense] boolValue] == YES)) {
 
        if (gregorianCalendar == nil) {
            gregorianCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
        }
        NSDateComponents *components = [gregorianCalendar components:NSYearCalendarUnit
                                                            fromDate:myBirthday
                                                              toDate:[NSDate date]
                                                             options:0];
        int years = [components year];
 
        if (years < 16) {
 
            valid = NO;
 
            // don't create an error if none was requested
            if (error != NULL) {
 
                NSBundle *myBundle = [NSBundle bundleForClass:[self class]];
                NSString *drivingAgeErrorString = [myBundle localizedStringForKey:@"TooYoungToDriveError"
                                  value:@"Person is too young to have a driving license."
                                  table:@"PersonErrorStrings"];
 
                NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
                [userInfo setObject:drivingAgeErrorString forKey:NSLocalizedFailureReasonErrorKey];
                [userInfo setObject:self forKey:NSValidationObjectErrorKey];
 
                NSError *drivingAgeError = [NSError errorWithDomain:PERSON_DOMAIN
                                                               code:NSManagedObjectValidationError
                                                           userInfo:userInfo];
 
                // if there was no previous error, return the new error
                if (*error == nil) {
                    *error = drivingAgeError;
                }
                // if there was a previous error, combine it with the existing one
                else {
                    *error = [self errorFromOriginalError:*error error:drivingAgeError];
                }
            }
        }
    }
    return valid;
}

Объединение ошибок проверки

Если существуют многократные отказы проверки в единственной работе, Вы создаете и возвращаете «многократную ошибочную ошибку» — т.е. NSError объект с кодом NSValidationMultipleErrorsError. Вы добавляете отдельные ошибки к массиву и добавляете массив — использование ключа NSDetailedErrorsKey— к пользовательскому информационному словарю в NSError объект. Этот образец также применяется к ошибкам, возвращенным методом проверки суперкласса. В зависимости от того, сколько тестов Вы выполняете, может быть удобно определить метод, комбинирующий существующее NSError объект (который может самостоятельно быть многократной ошибочной ошибкой) с новым и возвращает новую многократную ошибочную ошибку.

Следующий пример показывает реализацию простого метода объединить две ошибки в единственную многократную ошибочную ошибку. То, как комбинация сделана, зависит от того, была ли исходная ошибка самостоятельно многократной ошибочной ошибкой.

Перечисление 2  метод для объединения двух ошибок в единственную многократную ошибочную ошибку

- (NSError *)errorFromOriginalError:(NSError *)originalError error:(NSError *)secondError
{
    NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
    NSMutableArray *errors = [NSMutableArray arrayWithObject:secondError];
 
    if ([originalError code] == NSValidationMultipleErrorsError) {
 
        [userInfo addEntriesFromDictionary:[originalError userInfo]];
        [errors addObjectsFromArray:[userInfo objectForKey:NSDetailedErrorsKey]];
    }
    else {
        [errors addObject:originalError];
    }
 
    [userInfo setObject:errors forKey:NSDetailedErrorsKey];
 
    return [NSError errorWithDomain:NSCocoaErrorDomain
                               code:NSValidationMultipleErrorsError
                           userInfo:userInfo];
}