lançando uma exceção no objective-c / cacau

Qual é a melhor maneira de lançar uma exceção no objective-c / cacau?

Eu uso [NSException raise:format:] seguinte maneira:

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

Uma palavra de caucanvas aqui. Em Objective-C, ao contrário de muitos idiomas semelhantes, você geralmente deve evitar o uso de exceções para situações de erro comuns que podem ocorrer em operação normal.

A documentação da Apple para o Obj-C 2.0 afirma o seguinte: “Importante: Exceções são intensivas em resources em Objective-C. Você não deve usar exceções para controle de stream geral ou simplesmente para indicar erros (como um arquivo não acessível)”

A documentação conceitual de manipulação de Exceção da Apple explica o mesmo, mas com mais palavras: “Importante: Você deve reservar o uso de exceções para programação ou erros de tempo de execução inesperados, como access a coleção fora dos limites, tentativas de mutação de objects imutáveis, envio de uma mensagem inválida , e perdendo a conexão com o servidor de janelas.Você geralmente cuida desses tipos de erros com exceções quando um aplicativo está sendo criado, e não em tempo de execução. […] Em vez de exceções, objects de erro (NSError) e O mecanismo de entrega de erros do cacau é a maneira recomendada de comunicar os erros esperados nos aplicativos Cocoa. ”

As razões para isso são em parte aderir aos idiomas de programação em Objective-C (usando valores de retorno em casos simples e parâmetros de referência (geralmente a class NSError) em casos mais complexos), em parte que lançar e capturar exceções é muito mais caro e finalmente (e perpaps o mais importante) que exceções Objective-C são um wrapper fino em torno das funções setjmp () e longjmp () de C, essencialmente bagunçando seu tratamento cuidadoso da memory, veja esta explicação .

 @throw([NSException exceptionWith…]) 

Eu não tenho o representante para comentar sobre a resposta do eJames, então eu acho que preciso colocar o meu aqui. Para aqueles que vêm de um plano de fundo Java, você deve lembrar que o Java faz distinção entre Exception e RuntimeException. Exceção é uma exceção verificada e RuntimeException está desmarcada. Em particular, Java sugere o uso de exceções verificadas para “condições de erro normais” e exceções não verificadas para “erros de tempo de execução causados ​​por um erro do programador”. Parece que as exceções do Objective-C devem ser usadas nos mesmos lugares em que você usaria uma exceção não verificada, e os valores de retorno do código de erro ou os valores do NSError são preferidos nos locais em que você usaria uma exceção verificada.

Eu acho que para ser consistente é melhor usar o @throw com sua própria class que estende NSException. Então você usa as mesmas notações para try catch finally:

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

A Apple explica aqui como lançar e manipular exceções: Captura de exceções ao lançar exceções

Como o ObjC 2.0, as exceções Objective-C não são mais um wrapper para o setjmp () longjmp () do C e são compatíveis com a exceção C ++, o @try é “gratuito”, mas lançar e capturar exceções é muito mais caro.

De qualquer forma, as asserções (usando a família de macro NSAssert e NSCAssert) lançam o NSException, e são sensato usá-las como estados Ries.

Use o NSError para comunicar falhas e não exceções.

Pontos rápidos sobre o NSError:

  • O NSError permite que os códigos de erro de estilo C (inteiros) identifiquem claramente a causa raiz e esperamos que o manipulador de erro possa superar o erro. Você pode encapsular códigos de erro de bibliotecas C como SQLite em instâncias do NSError com muita facilidade.

  • O NSError também tem o benefício de ser um object e oferece uma maneira de descrever o erro com mais detalhes com seu membro do dictionary userInfo.

  • Mas o melhor de tudo, o NSError NÃO PODE ser lançado, por isso incentiva uma abordagem mais proativa ao tratamento de erros, em contraste com outras linguagens que simplesmente lançam a batata quente mais e mais acima na pilha de chamadas, ponto no qual só pode ser reportado ao usuário e não tratado de qualquer forma significativa (não se você acredita em seguir o maior princípio de ocultar informações que é OOP).

Link de referência : referência

Foi assim que aprendi com “The Big Nerd Ranch Guide (4ª edição)”:

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

Você pode usar dois methods para aumentar a exceção no bloco try catch

 @throw[NSException exceptionWithName]; 

ou o segundo método

 NSException e; [e raise]; 

Eu acredito que você nunca deve usar exceções para controlar o stream normal do programa. Mas exceções devem ser lançadas sempre que algum valor não corresponder a um valor desejado.

Por exemplo, se alguma function aceita um valor, e esse valor nunca é permitido ser nulo, então é bom traçar uma exceção em vez de tentar fazer algo ‘inteligente’ …

Ries

Você só deve lançar exceções se estiver em uma situação que indique um erro de programação e quiser impedir a execução do aplicativo. Portanto, a melhor maneira de lançar exceções é usando as macros NSAssert e NSParameterAssert e certificando-se de que NS_BLOCK_ASSERTIONS não esteja definido.

Exemplo de código para maiúsculas e minúsculas: @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:@" Test Exc" 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); } 

}

Usando:

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

Outro caso de uso mais avançado:

 - (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:@" Test Exc" 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 } 

}

Não há razão para não usar exceções normalmente no objective C, mesmo para indicar exceções de regras de negócios. A Apple pode dizer usar NSError quem se importa. O Obj C existe há muito tempo e, ao mesmo tempo, a documentação do ALL C ++ dizia a mesma coisa. A razão pela qual não importa o quão caro é arremessar e capturar uma exceção é que a vida útil de uma exceção é extremamente curta e … é uma EXCEÇÃO para o stream normal. Eu nunca ouvi ninguém dizer nunca na minha vida, cara essa exceção levou muito tempo para ser jogada e pego.

Além disso, há pessoas que pensam que o próprio objective C é muito caro e codificam em C ou C ++. Então, dizer sempre use NSError é mal informado e paranóico.

Mas a questão deste tópico ainda não foi respondida, o que é a melhor maneira de lançar uma exceção. As maneiras de retornar o NSError são óbvias.

Então é isso: [NSException raise: … @throw [[NSException alloc] initWithName …. ou @throw [[MyCustomException …?

Eu uso a regra marcada / desmarcada aqui um pouco diferente do que acima.

A diferença real entre (usando a metáfora java aqui) marcada / desmarcada é importante -> se você pode recuperar da exceção. E por recuperar quero dizer não apenas não falhar.

Então, eu uso classs de exceção personalizadas com @throw para exceções recuperáveis, porque é provável que eu tenha algum método de aplicativo procurando por certos tipos de falhas em vários blocos @catch. Por exemplo, se meu aplicativo for um checkbox eletrônico, eu teria um bloco @catch para o “RetiradaRequestExceedBalanceException”.

Eu uso NSException: raise para exceções de tempo de execução desde que eu não tenho como recuperar a exceção, exceto para pegá-lo em um nível superior e registrá-lo. E não faz sentido criar uma class personalizada para isso.

De qualquer forma, é o que eu faço, mas se há uma maneira melhor e expressiva, gostaria de saber também. No meu próprio código, desde que eu parei de codificar C há muito tempo atrás, eu nunca retornei um NSError mesmo que eu seja passado por uma API.