В чем причина паттерна Apple проверки возвращаемого значения, а не ошибки? - PullRequest
0 голосов
/ 18 января 2019

Руководство Apple по Использование и создание объектов ошибок дает следующий пример кода:

NSError *theError;
BOOL success = [myDoc writeToURL:[self docURL] ofType:@"html" error:&theError];

if (success == NO) {
    // Maybe try to determine cause of error and recover first.
    NSAlert *theAlert = [NSAlert alertWithError:theError];
   [theAlert runModal]; // Ignore return value.
}

и сопровождает его утверждением:

Важно: Успешность или неудача указывается значением, возвращаемым методом. Хотя методы Какао, которые косвенно возвращают объекты ошибок в области ошибок Какао, гарантированно будут возвращать такие объекты, если метод указывает на сбой, напрямую возвращая ноль или НЕТ, вы всегда должны проверять, чтобы возвращаемое значение было ноль или НЕТ, прежде чем пытаться что-либо делать с NSError object.

Мне всегда было интересно, почему этот шаблон так важен? Почему мы должны ВСЕГДА проверять возвращаемое значение? Что не так, если мы проверим, является ли ошибка нулевой или нет?

Ответы [ 2 ]

0 голосов
/ 23 января 2019

Представьте, что вы реализуете метод в терминах нескольких других методов:

-(BOOL)sendCachedRequestReturningError: (NSError**)err {
    BOOL success = [self readCachedRequestReturningError:err];
    if (!success && (*err).domain == MYFileDomain && (*err).errorCode == MYFileNotFoundCode) {
        success = [self sendUncachedRequestReturningError:err];
    }

    return success;
}

Теперь здесь есть 4 пути кода:

  1. Есть кешированный запрос. Мы просто вернемся success == YES и все хорошо.
  2. При попытке чтения из кэша возникает неисправимая ошибка. readCachedRequestReturningError: установит err и установит success == NO, и вызывающий абонент вызовет presentError: или что-то еще
  3. Произошла ошибка при попытке выполнить сетевой запрос. То же, что # 2, err установлено и success == NO.
  4. Кеша нет, но мы можем сделать сетевой запрос. readCachedRequestReturningError: установит err в действительное значение NSError{MYFileDomain, MYFileNotFoundCode}, но затем sendUncachedRequestReturningError: выполнится успешно и установит success == YES, а не коснется err вообще, оставив в нем предыдущую ошибку. Если вы теперь проверите err вместо проверки возвращаемого значения, вы будете думать, что произошла ошибка, когда все прошло хорошо.

Примечание: приведенный выше код сильно упрощен, потому что мы заботимся только об ошибках. Конечно, в реальной программе методы могут иметь другой возвращаемый параметр для фактического ответа на запрос или возвращать ответ или nil вместо success BOOL. Это также, вероятно, проверит, является ли err 1034 *.

0 голосов
/ 18 января 2019

Этот дизайн не так уж необычен, сравните также с ошибкой в ​​стандартном C.

Конструкция имеет ряд потенциальных преимуществ:

  • Функция не должна писать через указатель на успех. Это не только делает реализацию таких функций более легкой и менее подверженной ошибкам, но также может быть небольшим преимуществом в производительности (например, это предотвращает аннулирование кэшей ЦП при успешном выполнении функции).

  • Если мы всегда проверяем, что функция не сработала, прежде чем получить доступ к ошибке, мы можем использовать один и тот же указатель ошибки для нескольких функций. В противном случае мы можем получить предыдущий сбой, а не сбой самой последней функции.

  • Это облегчает написание кода проверки. Например. функция может установить ошибку по умолчанию. Если все проверки пройдены, функция может просто вернуть успех вместо того, чтобы сбрасывать переменную ошибки.

  • Функция может использовать тот же указатель ошибки при вызове других функций, но сбой этих помощников не обязательно означает сбой верхней функции.

В вашем конкретном случае переменная NSError *theError; не была инициализирована. Доступ к этой переменной без ее назначения вначале вызовет неопределенное поведение. Документация только гарантирует, что переменная будет установлена ​​в случае ошибки.

...