бросить исключение в цель-с / какао


Ответы:


528

Я использую [NSException raise:format:]следующим образом:

[NSException raise:@"Invalid foo value" format:@"foo of %d is invalid", foo];

9
Я предпочитаю этот способ, а не @throw([NSException exceptionWith…])подход, а более лаконичный.
Сэм Соффс

9
Обязательно прочитайте важное предупреждение от вреда ( stackoverflow.com/questions/324284/324805#324805 )
e.James

26
Я вообще тоже предпочитаю это, но есть одна ошибка. Может быть, это просто моя текущая версия Xcode, но синтаксис [NSException повышение ...], похоже, не распознается синтаксическим анализатором как выходной путь из метода, который возвращает значение. При использовании этого синтаксиса я вижу предупреждение «Элемент управления может достигнуть конца функции, не являющейся пустым», но с синтаксисом @throw ([NSException exceptionWith…]) синтаксический анализатор распознает это как выход и не отображает предупреждение.
Mattorb

1
@mpstx Я всегда использую синтаксис throw по той причине, которую вы указали (которая по-прежнему актуальна через два года в Xcode 4.6 и, вероятно, так будет всегда). Если среда IDE распознает, что создание исключения является точкой выхода из функции, часто имеет значение, если вы хотите избежать предупреждений.
Марк Амери

Я заметил, что блоки @ try / @ catch также приводят к ложноотрицательному значению для предупреждений «управление достигает конца функции, не являющейся пустотой» (т. Е. Предупреждение не отображается, когда оно должно быть)
Брайан Герстл,

256

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

Документация Apple для Obj-C 2.0 гласит следующее: «Важно: Исключения являются ресурсоемкими в Objective-C. Вы не должны использовать исключения для общего управления потоком данных или просто для обозначения ошибок (таких как недоступный файл)»

Концептуальная документация Apple по обработке исключений объясняет то же самое, но несколькими словами: «Важно: вам следует зарезервировать использование исключений для программирования или непредвиденных ошибок во время выполнения, таких как доступ за пределы коллекции, попытки изменить неизменяемые объекты, отправить недопустимое сообщение и потеря соединения с оконным сервером. Обычно такие ошибки исправляются с исключениями при создании приложения, а не во время выполнения. [.....] Вместо исключений используются объекты ошибок (NSError) и Механизм доставки ошибок Какао - рекомендуемый способ сообщить об ожидаемых ошибках в приложениях Какао. "

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


11
Я думаю, что это относится к большинству языков программирования: «старайтесь избегать использования исключений для общих ошибок». То же самое относится и к Java; плохая практика - обрабатывать ошибки пользовательского ввода (например) с исключениями. Не только из-за использования ресурсов, но и для ясности кода.
Beetstra

6
Что еще более важно, исключения в какао предназначены для указания на неисправимые программные ошибки. В противном случае это работает против фреймворка и может привести к неопределенному поведению. См. Stackoverflow.com/questions/3378696/iphone-try-end-try/… для получения подробной информации.
KPM

9
«То же самое относится и к Java;» Не согласен. Вы можете использовать проверенные исключения в Java просто отлично для нормальных условий ошибки. Конечно, вы не будете использовать исключения Runtime.
Даниэль Райан

Я предпочитаю способ Какао (исключения только для ошибок программиста), поэтому я предпочел бы делать это и на Java, но реальность такова, что вы должны придерживаться типичных практик в среде, а исключения для обработки ошибок торчат как в Objective-C плохо пахнет, но для этой цели часто используется в Java.
gnasher729

1
Этот комментарий не отвечает на вопрос. Может быть, OP просто хочет завершить работу приложения, чтобы проверить, работает ли структура отчета о сбоях, как ожидалось.
Симон

62
@throw([NSException exceptionWith…])

Xcode распознает @throwоператоры как точки выхода из функций, как returnоператоры. Использование @throwсинтаксиса позволяет избежать ошибочных предупреждений « Control может достигнуть конца не пустой функции », которые вы можете получить [NSException raise:…].

Кроме того, @throwможет использоваться, чтобы бросить объекты, которые не относятся к классу NSException.


11
@Steph Thirion: См developer.apple.com/documentation/Cocoa/Conceptual/Exceptions/... для всех деталей. Нижняя граница? Оба будут работать, но @throw можно использовать для выброса объектов, которые не относятся к классу NSException.
e.James

33

Что касается [NSException raise:format:]. Для тех, кто пришел из Java-фона, вы помните, что Java различает Exception и RuntimeException. Исключение является проверенным исключением, а RuntimeException не проверяется. В частности, Java предлагает использовать проверенные исключения для «нормальных условий ошибки» и непроверенные исключения для «ошибок времени выполнения, вызванных ошибкой программиста». Кажется, что исключения Objective C следует использовать в тех же местах, где вы использовали бы непроверенное исключение, а значения возврата кода ошибки или значения NSError предпочтительнее в тех местах, где вы будете использовать проверенное исключение.


1
Да, это правильно (через 4 года: D), создайте свой собственный класс ошибок ABCError, который выходит из класса NSError, и используйте его для проверенных исключений, а не NSExceptions. Поднимите NSExceptions, где ошибки программиста (неожиданная ситуация, такая как проблема формата числа) происходят.
Чатурам

15

Я думаю, что если вы согласны, лучше использовать @throw с вашим собственным классом, который расширяет NSException. Затем вы используете те же записи для try catch, наконец:

@try {
.....
}
@catch{
...
}
@finally{
...
}

Apple объясняет, как создавать и обрабатывать исключения: перехват исключений


я все еще
получаю

14

Начиная с ObjC 2.0, исключения Objective-C больше не являются оболочкой для setjmp () longjmp () C и совместимы с исключением C ++, @try является «бесплатным», но создание и отлов исключений намного дороже.

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


Хорошо знать! У нас есть сторонняя библиотека, которую мы не хотим модифицировать, которая генерирует исключения даже для самых маленьких ошибок. Мы должны поймать их в одном месте в приложении, и это просто заставляет нас съеживаться, но это заставляет меня чувствовать себя немного лучше.
Юрий Бриганс

8

Используйте NSError для сообщения об ошибках, а не об исключениях.

Краткие сведения о NSError:

  • NSError позволяет кодам ошибок в стиле C (целым числам) четко идентифицировать основную причину и, надеюсь, позволяет обработчику ошибок устранить ошибку. Вы можете легко обернуть коды ошибок из библиотек C, таких как SQLite, в экземпляры NSError.

  • NSError также имеет то преимущество, что является объектом, и предлагает способ более подробного описания ошибки с помощью своего словарного элемента userInfo.

  • Но лучше всего то, что NSError НЕ МОЖЕТ быть выброшен, поэтому он поощряет более упреждающий подход к обработке ошибок, в отличие от других языков, которые просто подбрасывают «горячую картошку» все дальше и дальше вверх по стеку вызовов, после чего об этом можно сообщить только пользователю и не обрабатывается каким-либо осмысленным образом (если вы не верите в то, что следите за самым большим принципом сокрытия информации ООП, который есть).

Ссылка Ссылка: Ссылка


Этот комментарий не отвечает на вопрос. Может быть, OP просто хочет завершить работу приложения, чтобы проверить, работает ли структура отчета о сбоях, как ожидалось.
Симон

7

Вот как я узнал об этом из «Руководства ранчо« Большой ботаник »(4-е издание)»:

@throw [NSException exceptionWithName:@"Something is not right exception"
                               reason:@"Can't perform this operation because of this or that"
                             userInfo:nil];

Хорошо, но это не говорит о многом userInfo:nil. :)
Cœur

6

Вы можете использовать два метода для вызова исключения в блоке try catch

@throw[NSException exceptionWithName];

или второй метод

NSException e;
[e raise];

3

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

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

Риз


0

Вы должны генерировать исключения только в том случае, если вы попали в ситуацию, которая указывает на ошибку программирования, и хотите остановить запуск приложения. Поэтому лучший способ генерировать исключения - использовать макросы NSAssert и NSParameterAssert и убедиться, что NS_BLOCK_ASSERTIONS не определен.


0

Пример кода для case: @throw ([NSExceptionceptionWithName: ...

- (void)parseError:(NSError *)error
       completionBlock:(void (^)(NSString *error))completionBlock {


    NSString *resultString = [NSString new];

    @try {

    NSData *errorData = [NSData dataWithData:error.userInfo[@"SomeKeyForData"]];

    if(!errorData.bytes) {

        @throw([NSException exceptionWithName:@"<Set Yours exc. name: > Test Exc" reason:@"<Describe reason: > Doesn't contain data" userInfo:nil]);
    }


    NSDictionary *dictFromData = [NSJSONSerialization JSONObjectWithData:errorData
                                                                 options:NSJSONReadingAllowFragments
                                                                   error:&error];

    resultString = dictFromData[@"someKey"];
    ...


} @catch (NSException *exception) {

      NSLog( @"Caught Exception Name: %@", exception.name);
      NSLog( @"Caught Exception Reason: %@", exception.reason );

    resultString = exception.reason;

} @finally {

    completionBlock(resultString);
}

}

С помощью:

[self parseError:error completionBlock:^(NSString *error) {
            NSLog(@"%@", error);
        }];

Еще один более продвинутый вариант использования:

- (void)parseError:(NSError *)error completionBlock:(void (^)(NSString *error))completionBlock {

NSString *resultString = [NSString new];

NSException* customNilException = [NSException exceptionWithName:@"NilException"
                                                          reason:@"object is nil"
                                                        userInfo:nil];

NSException* customNotNumberException = [NSException exceptionWithName:@"NotNumberException"
                                                                reason:@"object is not a NSNumber"
                                                              userInfo:nil];

@try {

    NSData *errorData = [NSData dataWithData:error.userInfo[@"SomeKeyForData"]];

    if(!errorData.bytes) {

        @throw([NSException exceptionWithName:@"<Set Yours exc. name: > Test Exc" reason:@"<Describe reason: > Doesn't contain data" userInfo:nil]);
    }


    NSDictionary *dictFromData = [NSJSONSerialization JSONObjectWithData:errorData
                                                                 options:NSJSONReadingAllowFragments
                                                                   error:&error];

    NSArray * array = dictFromData[@"someArrayKey"];

    for (NSInteger i=0; i < array.count; i++) {

        id resultString = array[i];

        if (![resultString isKindOfClass:NSNumber.class]) {

            [customNotNumberException raise]; // <====== HERE is just the same as: @throw customNotNumberException;

            break;

        } else if (!resultString){

            @throw customNilException;        // <======

            break;
        }

    }

} @catch (SomeCustomException * sce) {
    // most specific type
    // handle exception ce
    //...
} @catch (CustomException * ce) {
    // most specific type
    // handle exception ce
    //...
} @catch (NSException *exception) {
    // less specific type

    // do whatever recovery is necessary at his level
    //...
    // rethrow the exception so it's handled at a higher level

    @throw (SomeCustomException * customException);

} @finally {
    // perform tasks necessary whether exception occurred or not

}

}


-7

Нет никаких причин не использовать исключения, как правило, в цели C, даже для обозначения исключений бизнес-правил. Apple может сказать, используйте NSError, кто заботится. Obj C существует уже давно, и когда-то ВСЯ документация C ++ говорила одно и то же. Причина, по которой не имеет значения, насколько дорого выбрасывать и перехватывать исключение, заключается в том, что время жизни исключения чрезвычайно мало и ... его ИСКЛЮЧЕНИЕ для обычного потока. Я никогда не слышал, чтобы кто-то говорил в моей жизни, что человеку, которому это исключение потребовалось много времени, чтобы его бросили и поймали.

Кроме того, есть люди, которые думают, что цель C сама по себе слишком дорога, и вместо этого код на C или C ++. Таким образом, говоря, что всегда использовать NSError плохо информирован и параноидален.

Но на вопрос этой ветки еще не ответили, какой ЛУЧШИЙ способ вызвать исключение. Способы возврата NSError очевидны.

Так ли это: [повышение NSException: ... @throw [[выделение NSException] initWithName .... или @throw [[MyCustomException ...?

Я использую проверенное / непроверенное правило здесь немного иначе, чем выше.

Важна реальная разница между (используя метафору java здесь) проверенным / непроверенным -> можно ли восстановиться после исключения. И под восстановлением я подразумеваю не просто НЕ крах.

Поэтому я использую пользовательские классы исключений с @throw для восстанавливаемых исключений, потому что, скорее всего, у меня будет какой-то метод приложения, который ищет определенные типы сбоев в нескольких блоках @catch. Например, если мое приложение представляет собой банкомат, у меня будет блок @catch для «WithdrawalRequestExceedsBalanceException».

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

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


4
Я бы порекомендовал попытаться запрограммировать сервер с исключениями как часть обычного потока ошибок, прежде чем делать такие обобщающие утверждения, как «нет причин не использовать исключения, как правило, в цели C». Хотите верьте, хотите нет, но есть причины писать высокопроизводительные приложения (или, по крайней мере, части приложений) в ObjC, а создание исключений обычно серьезно снижает производительность.
jbenet

6
Есть действительно очень веские причины, почему бы не использовать исключения в Какао. См. Ответ Билла Бумгарнера здесь для более подробной информации: stackoverflow.com/questions/3378696/iphone-try-end-try/… . Он знает, о чем говорит (подсказка: проверьте своего работодателя). Исключения в Какао рассматриваются как неисправимые ошибки и могут приводить систему в нестабильное состояние. NSError - это способ обойти общие ошибки.
Брэд Ларсон

Исключения являются исключительными . Несоблюдение бизнес-правил, безусловно, не подходит. «Поиск и разработка кода с исключительными возможностями может привести к достойной победе». MSDN через codinghorror.com/blog/2004/10/…
Джонатан Уотмоф

3
Исключения нельзя выбрасывать из блоков. Исключения, создаваемые в среде ARC, могут привести к утечке вашей программы. Таким образом, отрицательный голос.
Мози

«Неважно, насколько дорого выбрасывать и ловить исключение», я пишу эмулятор, в котором критична производительность. Я не могу выбросить целую кучу дорогих исключений.
NobodyNada
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.