Objective-C Исключения

Я только что закончил курс программирования приложений для iPhone. В рамках курса я увидел

  • Objective-C предоставляет обработку исключений с помощью директивы @try
  • Системная библиотека не использует обработку исключений, предпочитая return nil

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

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

И что ты думаешь? Был ли мой тренер прав не использовать исключения Objective-C? Если да, то почему?

Ответ 1

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

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

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

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

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

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

Разъяснения для ARC

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

Поддержка исключений ARC может быть включена с помощью -fobjc-arc-exceptions или отключена с помощью -fno-objc-arc-exceptions. По умолчанию он отключен в Objective-C, но включен в Objective-C ++.

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

Это все из спецификации

Ответ 2

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

Кроме того, исключения, брошенные через код структуры, оставляют рамки в состоянии undefined. То есть вы не можете сделать что-то вроде:

void a() {
    @throw [MyException exceptionWithName: @"OhNoes!"  ....];
}

@try {
    ... call into framework code that calls a() above ...
} @catch (MyException e) {
    ... handle e ...
}

Нижняя строка:

Исключения не должны использоваться в Cocoa для управления потоком, проверки правильности ввода данных, определения достоверности данных или для указания обратных ошибок. Для этого вы используете шаблон NSError** как описанный здесь (спасибо Abizem).

(Обратите внимание, что существует небольшое количество API, которое нарушает это, - где исключение используется для указания восстанавливаемого состояния. Ошибки были поданы против них, чтобы обесценить и в конечном итоге удалить эти несоответствия.)


Наконец нашел документ, который я искал:

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

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

Ответ 3

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

И что касается возврата nil, это не все - функции, которые могут иметь проблемы, не просто возвращают nil, они также могут (и должны) предоставлять дополнительную информацию, используя объект NSError, который передается в как параметр.

См. также

Ответ 4

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

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

Однако вам нужно иметь в виду, что код других людей не гарантированно правильно обрабатывает исключение (если он чистый C, на самом деле он не может правильно обрабатывать исключения). Таким образом, вы никогда не должны позволять исключению распространяться за пределы вашего кода. Например, если вы выбрали исключение в глубине метода делегата, , вы должны обработать это исключение, прежде чем вернуться к отправителю сообщения делегата.

Ответ 5

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

Этот тип обработки ошибок может использоваться в сочетании с блоком try-catch внутри раздела кода:

-(NSDictionary *)getDictionaryWithID:(NSInteger)dictionaryID error:(NSError **)error
{
    @try
    {
         //attempt to retrieve the dictionary
    }
    @catch (NSException *e)
    {
        //something went wrong
        //populate the NSError object so it can be accessed
    }
}

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

Общее использование блока try-catch:

@try
{
    [dictionary setObject:aNullObject forKey:@"Key"];
}
@catch (NSException *e)
{
    //one of the objects/keys was NULL
    //this could indicate that there was a problem with the data source
}

Надеюсь, что это поможет.

Ответ 6

Я думаю, что ваш тренер прав. Вы можете делать всевозможные аргументы за исключениями и против исключений, но нижняя строка заключается в том, что если вы хотите, чтобы ваш код "обонял" право опытного разработчика Cocoa, вы будете применять свое приложение, используя те же шаблоны проектирования, которые Apple использует в своих кода и в их рамках. Это означает nil и NSError, а не исключения.

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

Другое дело, что a nil в Objective C обычно довольно "безопасен". То есть вы можете отправить любое сообщение в nil, и ваше приложение не будет аварийно завершено.

(Я должен также указать, что Apple использует исключения в некоторых местах, обычно там, где вы неправильно используете API.)

Ответ 7

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

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

Если бы я их использовал, я бы использовал их только в очень редких случаях и использовал коды ошибок или возвращал нуль, где это было возможно. плюс, я бы использовал их внутри библиотеки и не подвергал клиентов исключениям в интерфейсе (или побочным эффектам). я не использую их в objc или С++ (ну, я поймаю их, но я их не брошу).

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

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

исправление к исходному сообщению: cocoa libs предпочитают возвращать нуль, в некоторых случаях они будут генерировать исключения для вас (ловить).