Как преобразовать исключение в объект NSError

Я хочу преобразовать сообщение об исключении в объект NSError, чтобы использовать его в блоке try-catch (на самом деле я работаю над собственный модуль iOS для React Native).

RCT_EXPORT_METHOD(myMethod:(NSDictionary *)dict
             resolver:(RCTPromiseResolveBlock)resolve
             rejecter:(RCTPromiseRejectBlock)reject)
{
  @try {
    // Do something which could throw something (NS Error or NS Exception)
    resolve(nil);
  } @catch (NSException *exception) {
    // HERE I WANT TO TRANSFORM THE EXCEPTION exception INTO AN ERROR error
    NSError error = ???
    reject(@"my_error", @"Could not do something important", error);
  }
}

Я хочу преобразовать исключение в NSError, потому что третий параметр функции reject (которая отклоняет промис на стороне JS) ожидает, что ввод будет иметь тип NSError. Я не уверен, является ли мое решение (с использованием try-catch) лучшим в этом сценарии.

В руководстве разработчика Apple говорит

Вы можете преобразовать исключение в объект NSError, а затем представить информацию в объекте ошибки пользователю на панели предупреждений.

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

Итак, как мне преобразовать исключение в NSError? Справочник по API NSError не содержит подходящей функции.


person Andru    schedule 22.04.2017    source источник
comment
Как вы хотите использовать NSError? Вам действительно нужен NSError?   -  person rmaddy    schedule 22.04.2017
comment
@rmaddy Я отредактировал свой фрагмент кода, чтобы лучше отразить мой вариант использования, и объяснил, почему я хочу использовать NSError и не могу использовать NSException. Или я мог?   -  person Andru    schedule 22.04.2017


Ответы (2)


Вы не можете преобразовать NSException в NSError, потому что NSException у них разные свойства. Есть ли смысл ловить NSException вместо создания механизма **NSError? Исключения обычно являются фатальными и невосстановимыми, а ошибки нефатальными и исправимыми.

Если вам все равно нужно выполнить преобразование NSException в NSError, вы можете сделать это вручную следующим образом:

@try {
    // Something
} @catch (NSException *exception) {
    NSMutableDictionary * info = [NSMutableDictionary dictionary];
    [info setValue:exception.name forKey:@"ExceptionName"];
    [info setValue:exception.reason forKey:@"ExceptionReason"];
    [info setValue:exception.callStackReturnAddresses forKey:@"ExceptionCallStackReturnAddresses"];
    [info setValue:exception.callStackSymbols forKey:@"ExceptionCallStackSymbols"];
    [info setValue:exception.userInfo forKey:@"ExceptionUserInfo"];

    NSError *error = [[NSError alloc] initWithDomain:yourdomain code:errorcode userInfo:info];
    //use error
}
person Oskar    schedule 22.04.2017
comment
Причина ловли NSException заключается в том, что выбрасывается. - person rmaddy; 22.04.2017
comment
Извините, исходя из Swift, я забыл, что только NSExceptions можно поймать в Obj-C. Я обновил свой ответ. Он должен предоставить вам все необходимое для преобразования вашего исключения в ошибку. - person Oskar; 22.04.2017
comment
@Оскар, ты можешь поймать и NSError, и NSException. Либо с одним предложением @catch(id e), либо с одним предложением для каждого (@try { ... } @catch(NSError err) { ... } @catch(NSException ex) { ... }) - person Kevin; 29.06.2018
comment
@kevin oskar пытался сказать, что если выбрасывается NSException *, вы не можете поймать его с помощью предложения NSError * - person Erik Aronesty; 21.01.2020

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

/// NSExcetion * e = ...;
[NSError errorWithDomain:e.name code:0 userInfo:@{
    NSUnderlyingErrorKey: e,
    NSDebugDescriptionErrorKey: e.userInfo ?: @{ },
    NSLocalizedFailureReasonErrorKey: (e.reason ?: @"???") }];
}

Заголовочные файлы говорят о NSUnderlyingErrorKey:

Значение этого ключа должно быть NSError.

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

А для тех, кто недоумевает, что означают e.userInfo ?: @{ } и e.reason ?: @"???", это просто более короткий способ написания:

e.reason != nil ? e.reason : @"???"

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

person Mecki    schedule 15.11.2019
comment
Должен ли NSDebugDescriptionErrorKey: e.userInfo быть не NSDebugDescriptionErrorKey: e.description ? - person Leslie Godwin; 31.01.2020
comment
@LeslieGodwin -desrciption — это метод NSObject, который просто означает «Дайте мне печатную строку, описывающую этот объект, который может быть практически любым». Тем не менее, userInfo исключения может содержать много полезной информации о том, как, почему и где произошло это исключение, но формат зависит от исключения. Значение NSDebugDescriptionErrorKey печатается в различных местах отладки как %@, которое вызывает -description для своего значения и дает вам красивую удобочитаемую распечатку словаря userInfo исключения. - person Mecki; 31.01.2020
comment
Я думаю, что это плохой выбор для перевода имени исключения в домен ошибки - они несовместимы, и я бы сказал, что программист должен указать конкретный домен ошибки для своего приложения или библиотеки или даже рабочего процесса. Кроме того, я бы использовал как можно больше данных из NSException, чтобы обогатить userInfo тем, что я знаю об исключении, используя пользовательские ключи. - person Motti Shneor; 19.04.2021