Использование обработки исключений в сравнении с NSError в приложениях Cocoa - PullRequest
11 голосов
/ 05 января 2010

Привет всем. Я читал предложения Apple о том, когда / где / как использовать NSError против @ try / @ catch / @ наконец. По сути, у меня сложилось впечатление, что Apple считает, что лучше избегать использования языковых конструкций обработки исключений, кроме как в качестве механизма для остановки выполнения программы в непредвиденных ситуациях с ошибками (может быть, кто-то может привести пример такой ситуации?)

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

Одна вещь, от которой я зависаю, - это очистка памяти при возникновении ошибки. Во многих ситуациях (например, с использованием библиотек C, C ++, CoreFoundation и т. Д.) У вас есть много очистки памяти, которую необходимо выполнить перед выходом из строя функции из-за ошибки.

Вот пример, который я приготовил, который точно отражает ситуации, с которыми я сталкивался. Используя некоторые воображаемые структуры данных, функция открывает дескриптор файла и создает объект «MyFileRefInfo», который содержит информацию о том, что делать с файлом. Некоторые действия выполняются с файлом до закрытия дескриптора файла и освобождения памяти для структуры. Используя предложения Apple, у меня есть этот метод:

- (BOOL)doSomeThingsWithFile:(NSURL *)filePath error:(NSError **)error
{
  MyFileReference inFile; // Lets say this is a CF struct that opens a file reference
  MyFileRefInfo *fileInfo = new MyFileRefInfo(...some init parameters...);

  OSStatus err = OpenFileReference((CFURLRef)filePath ,&inFile);

  if(err != NoErr)
  {
    *error = [NSError errorWithDomain:@"myDomain" code:99 userInfo:nil];
    delete fileInfo;
    return NO;
  }

  err = DoSomeStuffWithTheFileAndInfo(inFile,fileInfo);

  if(err != NoErr)
  {
    *error = [NSError errorWithDomain:@"myDomain" code:100 userInfo:nil];
    CloseFileHandle(inFile); // if we don't do this bad things happen
    delete fileInfo;
    return NO;
  }      

  err = DoSomeOtherStuffWithTheFile(inFile,fileInfo);

  if(err != NoErr)
  {
    *error = [NSError errorWithDomain:@"myDomain" code:101 userInfo:nil];
    CloseFileHandle(inFile); // if we don't do this bad things happen
    delete fileInfo;
    return NO;
  }      

  CloseFileHandle(inFile);
  delete fileInfo;
  return YES;

}

Теперь ... моя логика Java говорит мне, что было бы лучше установить это как структуру try / catch / finally и поместить все вызовы, чтобы закрыть дескриптор файла и освободить память в блоке finally.

Вот так ..

    ...

    @try
    {
      OSStatus err = OpenFileReference((CFURLRef)filePath ,&inFile);
      if(err != NoErr)
      {
        ... throw some exception complete with error code and description ...
      }

      err = DoSomeStuffWithTheFileAndInfo(inFile,fileInfo);

      if(err != NoErr)
      {
         ... throw some exception ...
      }

      ... etc ...        
}
@catch(MyException *ex)
{
        *error = [NSError errorWithDomain:@"myDomain" code:[ex errorCode] userInfo:nil];
        return NO;
}
@finally
{
        CloseFileHandle(inFile); // if we don't do this bad things happen
        delete fileInfo;
}
return YES;

Неужели я схожу с ума от мысли, что это гораздо более элегантное решение с менее избыточным кодом? Я что-то пропустил?

Ответы [ 4 ]

17 голосов
/ 06 января 2010

Ответ Даниила верен, но этот вопрос заслуживает более грубого ответа.

Бросать исключение только в случае обнаружения неисправимой ошибки.

Используйте NSError при сообщении об ошибках, которые могут быть восстановлены из.

Любое исключение, которое выбрасывается через фрейм в рамках Apple, может привести к неопределенному поведению.

В центре разработки доступен документ по теме программирования исключений .

12 голосов
/ 05 января 2010

По сути, у меня сложилось впечатление, что Apple считает, что лучше избегать использования языковых конструкций для обработки исключений, кроме как в качестве механизма остановки выполнения программы в непредвиденных ситуациях с ошибками (может быть, кто-то может привести пример такой ситуации?)

Это не совсем мое впечатление. Я думал, что Apple предлагает использовать исключения для действительно исключительных условий, а NSError - для ожидаемых сбоев. Поскольку вы пришли из Java, я думаю, что NSError -> java.lang.Exception и Obj-C Exceptions -> java.lang.RuntimeException. Используйте исключение Obj-C, когда программист сделал что-то не так (например, неправильно использовал API), и используйте NSError, когда произошел ожидаемый сбой (например, не удалось найти удаленный сервер).

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

3 голосов
/ 05 января 2010

Исключения в Objective-C исторически были «тяжелыми», с затратами на производительность при вводе блока try, затратами на бросание, затратами на использование finally и т. Д. В результате разработчики какао обычно избегали исключений вне о нет, в небе падают ситуации - если файл отсутствует, используйте NSError, но если нет файловой системы и отрицательный объем свободной памяти, это исключение.

Это исторический взгляд. Но если вы создаете 64-битное приложение на 10.5 или новее, архитектура исключений была переписана так, чтобы она была «нулевой стоимостью», что может означать, что историческое представление больше не актуально. Как и во всем, это сводится к различным факторам - если работа в одну сторону более естественна для вас и позволит вам быстрее закончить, и если у вас нет каких-либо проблем, связанных с производительностью, и если она немного несовместимо с «традиционным» кодом Objective-C не беспокоит вас ... тогда нет никаких причин не использовать исключения.

2 голосов
/ 03 июля 2010

Согласно More iPhone 3 Development Дейва Марка и Джеффа ЛеМарша, исключения в используются только для действительно исключительных ситуаций и обычно указывают на проблему в вашем коде. Вы никогда не должны использовать исключения, чтобы сообщить о запуске -собственное состояние ошибки. Исключения используются в Objective-C с гораздо меньшей частотой, чем во многих других языках, таких как Java и C ++.

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

Вот пример, где вы бы использовали исключение:

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

NSException *ex = [NSException exceptionWithName:@"Abstract Method Not Overridden" reason:NSLocalizedString(@"You MUST override the save method", @"You MUST override the save method") userInfo:nil];
[ex raise];

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

...