2009-09-03 18 views

Respuesta

26

El amplio uso de NSAssert() no convertirá ObjC en Eiffel, pero sigue siendo una práctica bastante buena, siempre y cuando se tenga en cuenta cómo se implementa realmente y qué está haciendo. Cosas que debe tener en cuenta acerca de NSAssert():

Xcode no desactiva NSAssert() en el modo de lanzamiento de forma predeterminada. Debe recordar agregar NS_BLOCK_ASSERTIONS al GCC_PREPROCESSOR_DEFINITIONS en su xcconfig. (Usted es using xcconfigs, ¿verdad?) Un problema común es afirmar no-nil en casos en que nil funcionará en silencio; esto puede significar accidentes de campo para cosas que podrían haberse recuperado con gracia. Esto no está relacionado con la macro NDEBUG utilizada por assert(), y debe recordar definir ambas si su código incluye ambos tipos de aserciones.

Si compila NSAssert() en modo de lanzamiento, no obtiene ninguna indicación de dónde ocurrió un problema cuando los clientes le envían sus registros. Personalmente envuelvo NSAssert() en mis propias macros que siempre se registran, incluso en modo Release.

NSAssert() a menudo fuerza la lógica duplicada. Considere el caso de probar un puntero de C++ para NULL. Utiliza NSAssert(), pero todavía necesita utilizar una prueba simple if() para evitar colisiones en el campo. Este tipo de código duplicado puede convertirse en la fuente de errores, y he visto código que falla debido a las afirmaciones que ya no son válidas. (Afortunadamente, esto es generalmente en modo de depuración!) He debatido mucho sobre cómo crear una macro que combine la aserción y if(), pero es difícil hacerlo sin ser frágil o hacer que el código sea difícil de entender.

Debido a la última cuestión, generalmente pongo "NSAssert(NO, ...)" en una cláusula else{} en lugar de realizar la aserción en la parte superior del método. Esto no es ideal porque aleja el contrato de la firma del método (reduciendo así su beneficio documental), pero es el enfoque más efectivo que he encontrado en la práctica.

+0

no veo por qué sería terminar con código duplicado. Si la afirmación falla, su código no tiene la obligación de salvarse de un bloqueo. De hecho, no tiene sentido, porque una suposición que ha realizado se ha roto, por lo que no hay una manera sensata de evitar que el programa falle más tarde, debido a un estado incoherente. –

+0

Su código siempre tiene la obligación de comportarse bien para el usuario. Si bien en algunos casos la preocupación por la corrupción de datos significa que el bloqueo es el único enfoque. Pero en muchos casos esto no es cierto y recuperar, mostrar un error o incluso no hacer nada es preferible en Release vs crashing y dejando al usuario mirando a Springboard sin información. He visto suficientes afirmaciones incorrectas en el código de producción como para ser muy cauteloso con el bloqueo preventivo en Release en ausencia de posibles daños a los datos. Debug es una situación completamente diferente, y yo abogo por fallar rápidamente. –

+0

Una buena aplicación reconoce que tiene errores y busca contenerlos. Solo porque tengo un error menor en el sistema de ayuda no debería significar que todo el juego se cuelgue solo porque técnicamente el "programa no está definido". Cada programa real de cualquier tamaño implica bastante comportamiento indefinido. Es nuestra responsabilidad minimizar eso, pero también vivir dentro de eso y no colapsar cada vez que algo se vea extraño. –

7

Depuración. Cada vez que escribes código, casi siempre estás haciendo suposiciones. Suposiciones sobre el estado del medio ambiente, los valores de sus parámetros, sus variables y campos locales, etc. A menudo, estas suposiciones son incorrectas (un viejo colega me dio una buena máxima, que "La suposición es la madre de todos los fsckups") .

Existen aserciones para validar sus suposiciones, en el momento en que las realiza. Tiene un método

void foo(int x) 
{ 
    … 
} 

que sabe y ha documentado que solo funciona para x> 5? ¡Afírmalo!

Las afirmaciones viven junto con las pruebas unitarias y los métodos formales como parte de una buena práctica de codificación. Mientras que algunos podrían pensar que las pruebas de unidades completas aseguran que las afirmaciones son redundantes, no es el caso. Por ejemplo, puede asumir un método que, digamos, los empleados siempre tienen más de 16 y menos de 100 años, pero que el código, por el momento, no lo requiere. Las pruebas unitarias que pasen esos parámetros tendrán éxito, pero más adelante cuando necesite usar su suposición, tendrá un código en todas partes que aprobó las pruebas, pero está equivocado.

9

La principal advertencia son los efectos secundarios.Si se escribe:

NSAssert([self.navigationController popViewControllerAnimated:YES]!=nil,@"Something fails"); 

popViewControllerAnimated: se ejecutarán en la versión de depuración, pero no en la versión de lanzamiento que despoja NSAssert(). Esto significa que su versión de lanzamiento se comportará de manera diferente a la versión de depuración.

Este problema desaparece si usted es lo suficientemente cuidadoso:

UIViewController* vc = [self.navigationController popViewControllerAnimated:YES]; 
NSAssert(vc!=nil,@"Something fails"); 
+0

+1 perspicaz, aunque nunca llamo a métodos en afirmaciones. –

Cuestiones relacionadas