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.