¿Cuál es la mejor manera de lanzar una excepción en Objective-C / Cocoa?
objective-c
cocoa
exception-handling
Steph Thirion
fuente
fuente
@throw([NSException exceptionWith…])
enfoque ya que es más conciso.Una palabra de precaución aquí. En Objective-C, a diferencia de muchos lenguajes similares, generalmente debe tratar de evitar el uso de excepciones para situaciones de error comunes que pueden ocurrir en la operación normal.
La documentación de Apple para Obj-C 2.0 establece lo siguiente: "Importante: las excepciones son intensivas en recursos en Objective-C. No debe utilizar excepciones para el control de flujo general, o simplemente para indicar errores (como un archivo no accesible)"
La documentación conceptual de manejo de excepciones de Apple explica lo mismo, pero con más palabras: "Importante: debe reservar el uso de excepciones para la programación o errores inesperados de tiempo de ejecución tales como acceso a colecciones fuera de límites, intentos de mutar objetos inmutables, enviar un mensaje no válido y perder la conexión con el servidor de Windows. Por lo general, se ocupa de este tipo de errores con excepciones cuando se crea una aplicación en lugar de en tiempo de ejecución. [.....] En lugar de excepciones, objetos de error (NSError) y El mecanismo de entrega de errores de cacao es la forma recomendada de comunicar los errores esperados en las aplicaciones de cacao ".
Las razones para esto es en parte para adherirse a los modismos de programación en Objective-C (usando valores de retorno en casos simples y parámetros de referencia (a menudo la clase NSError) en casos más complejos), en parte que lanzar y atrapar excepciones es mucho más costoso y Finalmente (y lo más importante es que) las excepciones de Objective-C son un envoltorio delgado alrededor de las funciones setjmp () y longjmp () de C, que esencialmente arruinan su manejo cuidadoso de la memoria, vea esta explicación .
fuente
Xcode reconoce las
@throw
declaraciones como puntos de salida de funciones, como lasreturn
declaraciones. El uso de la@throw
sintaxis evita las advertencias erróneas de " Control puede llegar al final de la función no nula " que puede obtener[NSException raise:…]
.Además,
@throw
se puede usar para lanzar objetos que no son de la clase NSException.fuente
En cuanto a
[NSException raise:format:]
. Para aquellos que provienen de un fondo de Java, recordarán que Java distingue entre Exception y RuntimeException. La excepción es una excepción marcada y RuntimeException no está marcada. En particular, Java sugiere usar excepciones marcadas para "condiciones de error normales" y excepciones no verificadas para "errores de tiempo de ejecución causados por un error del programador". Parece que las excepciones de Objective-C deberían usarse en los mismos lugares donde usaría una excepción no verificada, y los valores de retorno del código de error o los valores de NSError se prefieren en los lugares donde usaría una excepción marcada.fuente
Creo que para ser consistente es mejor usar @throw con su propia clase que extiende NSException. Luego usas las mismas anotaciones para intentar atrapar finalmente:
Apple explica aquí cómo lanzar y manejar excepciones: Excepciones de captura Excepciones de lanzamiento
fuente
Desde ObjC 2.0, las excepciones de Objective-C ya no son un contenedor para setjmp () longjmp () y son compatibles con la excepción de C ++, @try es "gratis", pero lanzar y capturar excepciones es mucho más costoso.
De todos modos, las aserciones (usando la familia de macros NSAssert y NSCAssert) arrojan NSException, y es sensato usarlas como estados Ries.
fuente
Use NSError para comunicar fallas en lugar de excepciones.
Puntos rápidos sobre NSError:
NSError permite que los códigos de error de estilo C (enteros) identifiquen claramente la causa raíz y, con suerte, permitan que el controlador de errores supere el error. Puede envolver códigos de error de bibliotecas C como SQLite en instancias de NSError muy fácilmente.
NSError también tiene la ventaja de ser un objeto y ofrece una forma de describir el error con más detalle con su miembro de diccionario userInfo.
Pero lo mejor de todo es que NSError NO PUEDE lanzarse, por lo que fomenta un enfoque más proactivo para el manejo de errores, en contraste con otros lenguajes que simplemente arrojan la papa caliente más arriba y más arriba en la pila de llamadas, en cuyo punto solo se puede informar al usuario y no se maneja de manera significativa (no si cree en seguir el principio más grande de ocultación de información de OOP que es).
Enlace de referencia : Referencia
fuente
Así es como lo aprendí de "The Big Nerd Ranch Guide (4th edition)":
fuente
userInfo:nil
. :)Puede usar dos métodos para generar excepciones en el bloque try catch
o el segundo método
fuente
Creo que nunca debe usar Excepciones para controlar el flujo normal del programa. Pero se deben lanzar excepciones siempre que algún valor no coincida con el valor deseado.
Por ejemplo, si alguna función acepta un valor, y ese valor nunca se permite que sea nulo, entonces está bien lanzar una excepción en lugar de intentar hacer algo 'inteligente' ...
Ries
fuente
Solo debe lanzar excepciones si se encuentra en una situación que indica un error de programación y desea detener la ejecución de la aplicación. Por lo tanto, la mejor manera de generar excepciones es usar las macros NSAssert y NSParameterAssert, y asegurarse de que NS_BLOCK_ASSERTIONS no esté definido.
fuente
Código de muestra para el caso: @throw ([NSException exceptionWithName: ...
}
Utilizando:
Otro caso de uso más avanzado:
}
fuente
No hay ninguna razón para no usar excepciones normalmente en el objetivo C, incluso para significar excepciones de reglas comerciales. Apple puede decir usar NSError a quién le importa. Obj C ha existido durante mucho tiempo y, en un momento, TODA la documentación de C ++ decía lo mismo. La razón por la que no importa cuán costoso es lanzar y atrapar una excepción, es que la vida útil de una excepción es extremadamente corta y ... es una EXCEPCIÓN al flujo normal. Nunca he oído a nadie decir en mi vida, hombre, esa excepción tardó mucho tiempo en ser arrojada y atrapada.
Además, hay personas que piensan que el objetivo C en sí es demasiado costoso y en su lugar codifica en C o C ++. Por lo tanto, decir que siempre use NSError está mal informado y es paranoico.
Pero la pregunta de este hilo aún no ha sido respondida, ¿cuál es la MEJOR forma de lanzar una excepción? Las formas de devolver NSError son obvias.
Entonces, ¿es: [NSException raise: ... @throw [[NSException alloc] initWithName .... o @throw [[MyCustomException ...?
Utilizo la regla marcada / no marcada aquí ligeramente diferente que la anterior.
La diferencia real entre (usando la metáfora de Java aquí) marcada / desmarcada es importante -> si puede recuperarse de la excepción. Y por recuperación me refiero no solo a NO chocar.
Por lo tanto, utilizo clases de excepción personalizadas con @throw para excepciones recuperables, porque es probable que tenga algún método de aplicación que busque ciertos tipos de fallas en múltiples bloques @catch. Por ejemplo, si mi aplicación es un cajero automático, tendría un bloque @catch para "WithdrawalRequestExceedsBalanceException".
Uso NSException: raise para excepciones de tiempo de ejecución ya que no tengo forma de recuperarme de la excepción, excepto para capturarla en un nivel superior y registrarla. Y no tiene sentido crear una clase personalizada para eso.
De todos modos, eso es lo que hago, pero si hay una forma mejor y similarmente expresiva, me gustaría saber también. En mi propio código, desde que dejé de codificar C hace mucho tiempo, nunca devuelvo un NSError, incluso si una API me pasa uno.
fuente