Обработка исключений во всем приложении

У меня есть некоторые сомнения относительно обработки исключений в iPhone. Вот они:

  1. Предположим, у меня есть цепочка методов, которые вызываются один за другим, то есть метод A вызывает метод B, который, в свою очередь, вызывает метод C, который вызывает метод D. Это лучшее место для размещения моего блока try-catch ( это метод A, B, C, D или все они). Кроме того, мне нужно отобразить предупреждение для пользователя о том, что произошло исключение, а затем я хочу зарегистрировать это исключение на своем сервере. Итак, если я пишу свой блок try - catch во всех этих методах и если в методе D возникает исключение; тогда я думаю, что предупреждение будет отображаться 4 раза, и веб-служба для ведения журнала также будет вызываться 4 раза (пока управление не достигнет блока catch метода A). Итак, я должен просто использовать @throw; в блоке catch методов B, C и D и напишите мою логику в блоке catch метода A (метод верхнего уровня), или я должен вообще избегать написания try - catch в методах B, C и D.

  2. Мне нужен какой-то код ошибки из исключения (потому что моему веб-сервису нужны параметры, код ошибки и описание). Можно ли преобразовать исключение в ошибку или мне нужно будет жестко закодировать этот код?

  3. Я где-то читал о NSSetUncaughtExceptionHandler. И я думаю, если я могу установить этот обработчик (в методе appDidFinishLaunching делегата приложения) и в методе обработчика, если я покажу пользователю какое-то предупреждение и вызову веб-службу; тогда мне не нужно писать блок try - catch в каждом из моих методов, в каждом из моих классов. Я прав??

  4. Если произошло исключение и я написал либо блок try - catch, либо NSSetUncaughtExceptionHandler, мое приложение продолжит работу или оно не будет реагировать ни на одно из пользовательских событий. (Я уверен, что он справится со сбоем. Я хочу знать, зависнет ли он)

Кто-нибудь, пожалуйста, просветите меня по этой ТЕМЕ ИСКЛЮЧЕНИЯ.


person anshul    schedule 06.12.2011    source источник


Ответы (1)


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

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

2) Вы не можете преобразовать NSException в NSError (напрямую), потому что NSException не имеет всех свойств, которыми обладает NSError - это другое представление данных. Во-первых, код ошибки недоступен. Во-вторых, описание не локализовано. Лучшее, что вы можете сделать, - это создать код ошибки и домен, затем использовать необходимые свойства из NSException и сохранить их в NSError. Это может выглядеть примерно так:

// error checking omitted
extern NSString* const MONExceptionHandlerDomain;
extern const int MONNSExceptionEncounteredErrorCode;

NSError * NewNSErrorFromException(NSException * exc) {
    NSMutableDictionary * info = [NSMutableDictionary dictionary];
    [info setValue:exc.name forKey:@"MONExceptionName"];
    [info setValue:exc.reason forKey:@"MONExceptionReason"];
    [info setValue:exc.callStackReturnAddresses forKey:@"MONExceptionCallStackReturnAddresses"];
    [info setValue:exc.callStackSymbols forKey:@"MONExceptionCallStackSymbols"];
    [info setValue:exc.userInfo forKey:@"MONExceptionUserInfo"];

    return [[NSError alloc] initWithDomain:MONExceptionHandlerDomain code:MONNSExceptionEncounteredErrorCode userInfo:info];
}

@catch (NSException * exc) {
    NSError * err = NewNSErrorFromException(exc);
    ...
}

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

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

4) В большинстве случаев ваше приложение находится в нестабильном состоянии, и вам не следует продолжать работу. Но, чтобы на самом деле ответить на этот вопрос для этих угловых случаев: «Да, вы можете восстановиться и продолжить, когда поймаете, но вам следует пытаться восстановить и продолжить, только когда бросающий API сообщает, что восстановление поддерживается. Если проблема не зависит от вас, и проблема не является исключительной (например, файл не найден), и поставщик действительно ожидает, что вы продолжите, тогда я должен предположить, что они ожидают, что вы продолжите, даже если это действительно не (100% безопасно). ». Не пытайтесь восстановить / продолжить работу из обработчика исключений верхнего уровня (программа будет прервана после возврата). Если вы хотите изящно представить это сразу на OSX, лучше всего подойдет другой процесс. Если вы вызываете через чистый интерфейс C ++, то раскрутка четко определена, и необходимость перехвата необходима - продолжайте, если это можно восстановить. Исключения в C ++ могут быть исправлены и хорошо определены - они также используются довольно широко (включая не исключительные условия).

(IMO ...) Исключения в ObjC не следовало вводить, и любой метод, который генерируется из системных или сторонних библиотек, должен быть устаревшим. Они не раскручиваются хорошо или четко. Кроме того, разворачивающиеся потоки против обычного потока программы Какао. Это означает, что касание любой памяти / отношений объекта objc, которые были в мутации во время выброса и которые лежат между выбросом и захватом, ничем не хуже поведения undefined. Проблема в том, что вы не знаете, что это за память (в большинстве случаев и в разумные сроки). Исключения C ++ четко определены, и они корректно раскручиваются (например, вызываются деструкторы), но попытка продолжить в контексте ObjC игнорирует любые последствия неопределенного поведения. IMO, они должны существовать только для ObjC ++ (потому что C ++ требует их).

В идеальном мире ваши программы ObjC и библиотеки, которые вы используете, не будут использовать исключения (вообще). Поскольку вы используете библиотеки, которые генерируют выбросы (включая Какао), устанавливайте обработчик исключений верхнего уровня только в том случае, если вам нужна особая информация об ошибке. Если API требует, чтобы вы могли ожидать возникновения исключения из-за обстоятельств, не зависящих от вас, и ожидается восстановление, тогда напишите уловку, но немедленно преобразуйте эту логику в нормальный поток программы (например, NSError) - вы никогда не нужно писать свой собственный бросок. -[NSArray objectAtIndex: и «объект не реагирует на селектор» являются примерами ошибок программиста - их не следует не отлавливать, но программу следует исправить.

person justin    schedule 06.12.2011
comment
Привет, Джастин, большое спасибо за информацию. Но не могли бы вы рассказать мне об использовании NSSetUncaughtExceptionHandler и номер 2, если возникнет исключение, я должен либо прервать приложение, либо вывести его из строя. Я имею в виду, я не должен позволять пользователю продолжать его использовать. Кроме того, не могли бы вы рассказать мне, как преобразовать NSException в NSError. - person anshul; 06.12.2011
comment
Что такое клавиши MON *? Они определены в Какао? - person QED; 20.04.2016
comment
@QED нет. это просто префикс, который я использую в образце кода (например, вместо префиксов NS, CF, CG и т. д.) - person justin; 27.04.2016