Выбрасывание исключения в objective-c/cocoa

Какой лучший способ выбросить исключение в objective-c/cocoa?

Ответ 1

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

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

Ответ 2

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

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

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

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

Ответ 3

@throw([NSException exceptionWith…])

Ответ 4

У меня нет комментариев, чтобы прокомментировать ответ eJames, поэтому, я думаю, мне нужно поместить мое здесь. Для тех, кто исходит из фона Java, вы помните, что Java различает Exception и RuntimeException. Исключение - проверенное исключение, а исключение RuntimeException не отмечено. В частности, Java предлагает использовать проверенные исключения для "нормальных условий ошибки" и исключенных исключений для "ошибок времени выполнения, вызванных ошибкой программиста". Похоже, что исключения Objective-C должны использоваться в тех же местах, где вы использовали бы исключение, не считая исключения, а значения возврата кода ошибки или значения NSError предпочтительнее в тех местах, где вы должны использовать проверенное исключение.

Ответ 5

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

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

Apple объясняет, как бросать и обрабатывать исключения: Улавливание исключений Отбрасывание исключений

Ответ 6

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

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

Ответ 7

Использовать NSError для обмена отказами, а не с исключениями.

Быстрые точки о NSError:

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

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

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

Ссылка ссылки: Reference

Ответ 8

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

@throw[NSException exceptionWithName];

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

NSException e;
[e raise];

Ответ 9

Вот как я узнал об этом из "The Big Nerd Ranch Guide (4-е издание)":

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

Ответ 10

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

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

Риес

Ответ 11

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

Ответ 12

Пример кода для случая: @throw ([NSException exceptionWithName:...

- (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 handled at a higher level

    @throw (SomeCustomException * customException);

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

}

}

Ответ 13

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

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

Но вопрос об этом потоке еще не ответил, что лучший способ выбросить исключение. Способы возврата NSError очевидны.

Итак: [NSException raise:... @throw [[NSException alloc] initWithName.... или @throw [[MyCustomException...?

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

Настоящая разница между (с использованием java-метафоры здесь) checked/unchecked важна → можно ли восстановить из исключения. И, восстановившись, я имею в виду не просто НЕ сбой.

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

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

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