Обработка ошибок / исключений в методе, который возвращает bool

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

- (BOOL)getBoolValueForKey:(NSString *)key; 

Что делать, если вызывающий объект этого метода передает ключ, который не существует. Должен ли я бросать пользовательский ключ NSException, который не существует (но исключение бросания не рекомендуется в объективе c) или добавить параметр NSError к этому методу, как показано ниже?

 - (BOOL)getBoolValueForKey:(NSString *)key error:(NSError **)error; 

Если я использую NSError, мне придется возвращать «НЕТ», что будет вводить в заблуждение, так как «НЕТ» может быть допустимым значением любого действительного ключа.

6 Solutions collect form web for “Обработка ошибок / исключений в методе, который возвращает bool”

API для этого давно установлен NSUserDefaults и должен стать отправной точкой для разработки вашего API:

 - (BOOL)boolForKey:(NSString *)defaultName; 

Если логическое значение связано с именем по умолчанию в пользовательских значениях по умолчанию, это значение возвращается. В противном случае возвращается NO.

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

Традиционно, если вы хотите различать NO и nil , тогда вызовите objectForKey чтобы получить NSNumber и проверить NSNumber . Опять же, это поведение для многих магазинов ключей Cocoa, и его не следует легко менять.

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

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

Во-вторых, вы можете различать nil и NO , используя метод get . Ваш метод начинается с get , но это не должно. get означает «возвращает по ссылке» в Cocoa, и вы можете использовать его таким образом. Что-то вроде этого:

 - (BOOL)getBool:(BOOL *)value forKey:(NSString *)key { id result = self.values[key]; if (result) { if (value) { // NOTE: This throws an exception if result exists, but does not respond to // boolValue. That's intentional, but you could also check for that and return // NO in that case instead. *value = [result boolValue]; } return YES; } return NO; } 

Это занимает указатель на bool и заполняет его, если значение доступно, и возвращает YES . Если значение недоступно, оно возвращает NO .

Нет причин привлекать NSError . Это добавляет сложности, не придавая при этом никакой ценности. Даже если вы рассматриваете Swift-мост, я бы не использовал NSError здесь, чтобы получить throws . Вместо этого вы должны написать простую Swift-оболочку вокруг этого метода, который возвращает Bool? , Это гораздо более мощный подход и более простой в использовании на стороне Swift.

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

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

Если вы хотите сообщить ошибку времени выполнения, из которой программа может восстановить / продолжить выполнение, добавление указателя ошибки – это способ сделать это.

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

  • Если вы получаете доступ к этому API через Swift, NO всегда подразумевает, что возникает ошибка, даже если в вашей реализации Objective-C вы не заполнили указатель ошибки, то есть вам понадобится do / catch и дескриптор нулевого значения ошибка.
  • Противоположное действительно также допустимо, т. Е. Можно исключить ошибку в случае успеха (например, NSXMLDocument делает это для передачи некритических ошибок проверки). Насколько я знаю, нет возможности передавать эту некритичную информацию об ошибках в Swift.

Если вы намереваетесь использовать этот API из Swift, я бы, возможно, поставил BOOL в NULL-значение NSNumber (в этом случае ошибка будет равна нулю, и успешное событие NO будет NSNumber с NO, завернутым в него).

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

Вы определяете основную слабость подхода к обработке ошибок Apple.

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

 if (error) { // ... problem // handle error and/ or return } 

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

Это нехорошее решение, но лучшее, что я знаю.

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

Если вы хотите все это

  1. Различать случаи отказа и успеха
  2. Работайте с значением bool только в случае успеха
  3. В случае сбоя вызывающий абонент ошибочно не считает, что возвращаемое значение является значением ключа

Я предлагаю сделать реализацию на основе блоков. У вас есть successBlock и errorBlock чтобы четко разделить.

Caller вызовет метод, подобный этому

 [self getBoolValueForKey:@"key" withSuccessBlock:^(BOOL value) { [self workWithKeyValue:value]; } andFailureBlock:^(NSError *error) { NSLog(@"error: %@", error.localizedFailureReason); }]; 

и реализация:

 - (void)getBoolValueForKey:(NSString *)key withSuccessBlock:(void (^)(BOOL value))success andFailureBlock:(void (^)(NSError *error))failure { BOOL errorOccurred = ... if (errorOccurred) { // userInfo will change // if there are multiple failure conditions to distinguish between NSDictionary *userInfo = @{ NSLocalizedDescriptionKey: NSLocalizedString(@"Operation was unsuccessful.", nil), NSLocalizedFailureReasonErrorKey: NSLocalizedString(@"The operation timed out.", nil), NSLocalizedRecoverySuggestionErrorKey: NSLocalizedString(@"Have you tried turning it off and on again?", nil) }; NSError *error = [NSError errorWithDomain:@"domain" code:999 userInfo:userInfo]; failure(error); return; } BOOL boolValue = ... success(boolValue); } 

Мы используем это

 - (id) safeObjectForKey:(NSString*)key { id retVal = nil; if ([self objectForKey:key] != nil) { retVal = [self objectForKey:key]; } else { ALog(@"*** Missing key exception prevented by safeObjectForKey"); } return retVal; } 

Файл заголовка NSDictionary + OurExtensions.h

 #import <Foundation/Foundation.h> @interface NSDictionary (OurExtensions) - (id) safeObjectForKey:(NSString*)key; @end 

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

  • Как получить номер строки, имя метода и имя класса при сбое при использовании Objective C
  • libc abi.dylib: завершение с неперехваченным исключением типа NSException CollectionView
  • Как использовать XCTAssertThrowsSpecific
  • Захват исключения NSKeyedUnarchiver
  • Как заменить NSUncaughtExceptionHandler на определение в навигаторе точки останова?
  • Почему этот код разбивается на распределенное приложение, но работает в отладчике?
  • Исключение NSInvalidArgument - случайные объекты Получение didEnterBackground
  • iphone - попробуйте, поймайте вопрос
  • Objective-c Try / Catch not catching
  • Необработанный номер линии NSException?
  • PhoneC: Разработка iOS проста с помощью XCode, Swift3, UITableView, cocatouch, давайте создадим приложения для iPhone, iPad и Macbook.