Почему в примере кода NSOperation используется @try & @catch

В руководстве по программированию Apple Concurrency примеры подкласса NSOperation (как неконкурентные, так и параллельные варианты) используют обработку исключений, и мне интересно, почему они поощряют этот стиль в операциях.

Листинг 2-4. Ответ на запрос отмены

- (void)main {
   @try {
      BOOL isDone = NO;

      while (![self isCancelled] && !isDone) {
          // Do some work and set isDone to YES when finished
      }
   }
   @catch(...) {
      // Do not rethrow exceptions.
   }
}

Мое понимание заключается в том, что в большинстве случаев обработка исключений не является обычной практикой в ​​коде Objective-C - исключения - это, по сути, ошибки программиста, и это должно привести к сбою приложения, тогда как неожиданные входы лучше всего обрабатывать с помощью NSError. (Возможно, мое дезинформированное понимание исходит из таких вещей, как this и this)

Мне интересно, представляет ли NSOperations определенную ситуацию, в которой важна обработка исключений, или если это более предпочтительный стиль конкретного автора этого руководства.

В качестве побочного примечания, некоторые из примеров кода NSOperation следуют этому стилю, другие примеры этого не делают. Большинство высокопроизводительных OSS не используют исключения (например, AFNetworking).

Ответ 1

Ваше понимание правильное - NSError (или аналогичный) следует использовать для передачи информации об ошибках, а не исключений. Большинство Objective-C кода не являются безопасными для исключительных ситуаций и, по крайней мере, будут обладать ресурсами утечки. Как правило, никогда не позволяйте вашему коду утечка исключения в код другого пользователя - будь то Apple или третьи стороны. Некоторые сторонние структуры могут явно указывать, что они безопасны для исключения, но это редко.

По этому принципу вы можете понять, почему вы должны иметь обработчик исключений catch-all в вашем методе main независимо. Но есть и другая причина: ваша операция будет выполняться по выделенному потоку. Исключения, выброшенные из вашей операции, будут распространяться по стеку, но не дальше. Логический вызывающий или владелец операции не получит их, поскольку они работают в другом потоке (или вообще не работают). Таким образом, утечка исключений либо убьет всю вашу программу, либо будет проглочена без каких-либо других указаний. Ваша программа может застрять в странном состоянии - так как вы не понимали, что произошла ошибка, вы можете продолжать ждать результата своей операции, которая никогда не появится.

Кроме того, у Apple есть раздел в Concurrency Руководство по программированию, где они говорят о Обработка ошибок и исключений. Их первая точка на "дискретных сущностях" ссылается на то, что я сказал в предыдущем абзаце:

Обработка ошибок и исключений

Поскольку операции по существу дискретных объектов внутри вашего приложения, они несут ответственность за обработки любых ошибок или исключений, которые возникают. В OS X v10.6 и более поздних версиях, метод запуска по умолчанию, предоставляемый классом NSOperation, не исключение catch. (В OS X v10.5 метод запуска делает catch и исключайте исключения.) Ваш собственный код всегда должен улавливать и подавлять исключений. Он также должен проверять коды ошибок и уведомлять соответствующие части вашего приложения по мере необходимости. И если вы замените метод начала, вы должны также поймать любые исключения в своем для предотвращения их отказа от основной поток.

Среди типов ситуаций с ошибками вы должны быть готовы к следующие:

  • Проверить и обработать коды ошибок в стиле errno в стиле UNIX.
  • Проверить явную ошибку коды, возвращаемые методами и функциями.
  • Исключение исключений ваш собственный код или другие системные рамки.
  • Ловить исключения самим классом NSOperation, который выдает исключения из следующие ситуации:
    • Если операция не готова к выполнению, его метод запуска называется
    • Когда операция выполняется или завершена (возможно, потому что он был отменен), и его метод запуска называется еще раз
    • При попытке добавить блок завершения к операции, которая уже выполняется или завершено
    • Когда вы пытаетесь получить результат объект NSInvocationOperation, который был отменен

Если ваш пользовательский код сталкивается с исключением или ошибкой, вы должны предпринять любые шаги необходимы для распространения этой ошибки на остальную часть вашего приложения. Класс NSOperation не предоставляет явные методы для передачи по кодам ошибок или исключениям в других частях вашего выражение. Поэтому, если такая информация важна для вашей приложения, вы должны предоставить необходимый код.

Ответ 2

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

Небезопасно бросать исключения в обстоятельствах, когда ресурсы автоматически не управляется. Это относится к структуре Cocoa(и соседние рамки), поскольку они используют ручной подсчет ссылок.

Если вы выбросите исключение, любой выпуск, который вы пропустите, отключится стек приведет к утечке. Это должно ограничивать вас только если вы уверены, что не собираетесь восстанавливаться, поскольку все ресурсы возвращаются в ОС при завершении процесса.

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

Эта проблема уменьшается, если вы используете сборщик мусора Objective-C, поскольку любой ресурс, представленный объектом Objective-C, будет правильно выпущенный. Однако ресурсы C (такие как файловые дескрипторы или памяти, выделенной malloc), которые не завернуты в объект Objective-Cвсе еще будет течь.

Итак, в целом, не бросайте.

В API Cocoa есть несколько обходных решений, о которых вы говорили. Возвращение nil и шаблон NSError ** - два из них.