¿Cuál es el punto de NSAssert, en realidad?

155

Tengo que preguntar esto, porque: Lo único que reconozco es que si la afirmación falla, la aplicación falla. ¿Es esa la razón por la cual usar NSAssert? ¿O qué más es el beneficio de esto? ¿Y es correcto poner un NSAssert justo por encima de cualquier suposición que haga en el código, como una función que nunca debería recibir un -1 como parámetro, pero puede un -0.9 o -1.1?

TheNeil
fuente

Respuestas:

300

Afirmar es asegurarse de que un valor es lo que se supone que es. Si una afirmación falla, eso significa que algo salió mal y la aplicación se cierra. Una razón para usar aserción sería si tiene alguna función que no se comportará o creará efectos secundarios muy malos si uno de los parámetros que se le pasa no es exactamente algún valor (o un rango de valores) que puede poner una aserción para hacer asegúrese de que el valor es lo que espera que sea, y si no lo es, entonces algo está realmente mal, por lo que la aplicación se cierra. Assert puede ser muy útil para la depuración / prueba de unidad, y también cuando proporciona marcos para evitar que los usuarios hagan cosas "malvadas".

Daniel
fuente
9
Debería sacar NSAssert para su lanzamiento. Hay una bandera de tiempo de compilación para hacer eso.
Barry Wark
127
> Debería sacar NSAssert para su lanzamiento. Esto es discutible. Siempre publico mis aplicaciones con afirmaciones habilitadas, y esta es una práctica estándar para muchos programas, Apple, por ejemplo, hace esto. Tan pronto como su programa haya detectado un estado anormal, debe bloquearse. Puede obtener un seguimiento de la pila de dónde ocurrió el error, mientras que si deshabilita las afirmaciones, podría terminar corrompiendo la memoria y / o los datos del usuario y el problema será muy difícil de depurar.
Mike Weller
18
Tenga en cuenta que XCode 4 tiene NS_BLOCK_ASSERTIONS definido de forma predeterminada en las configuraciones de lanzamiento. Supongo que si no cambia que su código publicado no contendrá NSAssert: s.
Jonny
16
Si entiendo correctamente, ¿cuál es el punto de dejarlos (en la versión de lanzamiento)? ¿Por qué no reemplazar el NSAssert con una declaración if, y if (algo terrible sucede), entonces informar al usuario (o hacer algo que esté bajo su control), y no simplemente salir / bloquearse y dejar al usuario preguntándose qué pasó ... O ¿Me estoy perdiendo de algo?
Gik
11
Es una pérdida de tiempo del desarrollador seguir el camino de cada caso excepcional que no se supone que suceda en circunstancias normales. Esto implica pensar en formas apropiadas de informar al usuario para cada uno de ellos y / o hacer que la aplicación sea lo suficientemente robusta como para continuar de la manera esperada después de su ocurrencia. El enfoque más práctico es bloquear la aplicación y corregir el error encontrado en el informe de bloqueo y lanzar una nueva versión. Dicho esto, es importante asegurarse de que no haya pérdida de datos en tal situación. Sin embargo, esto debe garantizarse, pero es mucho menos trabajo.
trss
20

Realmente no puedo hablar con NSAssert, pero imagino que funciona de manera similar a la afirmación de C ().

afirmar () se utiliza para hacer cumplir un contrato semántico en su código. ¿Qué significa eso, preguntas?

Bueno, es como dijiste: si tienes una función que nunca debería recibir un -1, puedes hacer valer aserción ():

nulo gimme_positive_ints (int i) {
  afirmar (i> 0);
}

Y ahora verá algo como esto en el registro de errores (o STDERR):

Afirmación i> 0 fallida: archivo ejemplo.c, línea 2

Por lo tanto, no solo protege contra entradas potencialmente malas, sino que las registra de una manera útil y estándar.

Ah, y al menos en C afirmar () era una macro, por lo que podría redefinir afirmar () como no operativo en su código de lanzamiento. No sé si ese es el caso con NSAssert (o incluso afirmar () más), pero fue bastante útil compilar esos controles.

Mando Escamilla
fuente
2
Sí, NSAssert también es una macro.
Martin Wickman el
18

NSAssertte da más que simplemente bloquear la aplicación. Le indica la clase, el método y la línea donde ocurrió la aserción. Todas las aserciones también se pueden desactivar fácilmente utilizando NS_BLOCK_ASSERTIONS. Por lo tanto, es más adecuado para la depuración. Por otro lado, arrojar un NSExceptionsolo bloquea la aplicación. Tampoco informa sobre la ubicación de la excepción, ni puede deshabilitarse de manera tan simple. Vea la diferencia en las imágenes a continuación.

La aplicación se bloquea porque una aserción también genera una excepción, ya que la documentación de NSAssert establece:

Cuando se invoca, un controlador de aserciones imprime un mensaje de error que incluye el método y los nombres de clase (o el nombre de la función). Luego genera una excepción NSInternalInconsistencyException.

NSAssert:

Registros después de una afirmación

NSException:

Registros después de una excepción

Abdurrahman Mubeen Ali
fuente
Un NSExceptionofrece muchas oportunidades para personalizar la salida que devuelve a través de los parámetros reasony userInfo. No hay ninguna razón por la que no pueda agregar el nombre de la clase, el selector, la información de línea y cualquier otra cosa que desee agregar para ayudar con la depuración. En mi humilde opinión, se utiliza NSAssertpara fines de depuración durante el desarrollo, pero los deshabilita para enviar; arroja un NSExceptionsi desea dejar una afirmación en el código de envío.
markeissler
17

Además de lo que todos dijeron anteriormente, el comportamiento predeterminado de NSAssert()(a diferencia de C assert()) es lanzar una excepción, que puede atrapar y manejar. Por ejemplo, Xcode hace esto.

Jens Ayton
fuente
¿Hay más información sobre cómo podemos atrapar y manejar la excepción?
Gon
1
Las excepciones en el cacao NO son "atrapables y manejables" de facto. Si el control pasa a través de una función de manzana en cualquier parte del árbol de llamadas, el comportamiento no está definido. Las excepciones son puramente para informes de errores (también conocidos como crittercismo, etc.), no para uso general como en Java.
Michael
9

Solo para aclarar, como alguien mencionó pero no explicó por completo, la razón para tener y usar afirmaciones en lugar de simplemente crear código personalizado (hacer ifs y generar una excepción para datos incorrectos, por ejemplo) es que las afirmaciones DEBEN estar deshabilitadas para las aplicaciones de producción.

Durante el desarrollo y la depuración, las afirmaciones están habilitadas para que pueda detectar errores. El programa se detendrá cuando una afirmación se evalúe como falsa. Pero, cuando compila para la producción, el compilador omite el código de aserción y en realidad HACE QUE SU PROGRAMA SE EJECUTE MÁS RÁPIDO. Para entonces, con suerte, ya ha solucionado todos los errores. En caso de que su programa todavía tenga errores durante la producción (cuando las aserciones están deshabilitadas y el programa "omite" las aserciones), su programa probablemente terminará fallando en algún otro momento.

De la ayuda de NSAssert: "Las afirmaciones se deshabilitan si se define la macro del preprocesador NS_BLOCK_ASSERTIONS". Entonces, solo coloque la macro en su objetivo de distribución [solo].

Ohad Kravchick
fuente
6

NSAssert(y su equivalente stdlib assert) son para detectar errores de programación durante el desarrollo. Nunca debe tener una afirmación que falle en una aplicación de producción (lanzada). Por lo tanto, puede afirmar que nunca pasa un número negativo a un método que requiere un argumento positivo. Si la afirmación falla alguna vez durante la prueba, tiene un error. Sin embargo, si el usuario ingresa el valor pasado, debe realizar una validación adecuada de la entrada en lugar de confiar en la afirmación en producción (puede establecer un #definir para las compilaciones de lanzamiento que deshabilita) NSAssert*.

Barry Wark
fuente
2
¡+1 porque tu respuesta tiene más sentido para mí! Usar NSAssert tiene más sentido si es para uso de desarrollo, no después del lanzamiento. Un usuario que ingresa un valor que no está permitido debe ser seguido por un error de UI, no NSAssert que bloquea la aplicación. ¡La niebla se ha despejado!
pnizzle 01 de
3

Las aserciones se usan comúnmente para imponer el uso previsto de un método particular o una pieza de lógica. Digamos que estaba escribiendo un método que calcula la suma de dos enteros mayores que cero. Con el fin de asegurarse de que el método siempre se usó según lo previsto, es probable que ponga una afirmación que pruebe esa condición.

Respuesta corta: exigen que su código se use solo según lo previsto.

Salones
fuente
2

Para responder completamente a su pregunta, el objetivo de cualquier tipo de afirmación es ayudar a la depuración. Es más valioso detectar errores en su origen y luego detectarlos en el depurador cuando provocan bloqueos.

Por ejemplo, puede pasar un valor a una función que espera valores en un cierto rango. La función puede almacenar el valor para su uso posterior, y en el uso posterior, la aplicación se bloquea. La pila de llamadas que se ve en este escenario no muestra la fuente del valor incorrecto. Es mejor detectar el valor negativo a medida que entra para averiguar quién está pasando el valor negativo y por qué.

Robert Hawkey
fuente
-3

NSAsserthacer que la aplicación se bloquee cuando coincida con la condición. Si no coincide con la condición, se ejecutarán las siguientes declaraciones. Busque el EX a continuación:

Acabo de crear una aplicación para probar cuál es la tarea NSAssert:

    - (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    [self testingFunction:2];
}

-(void)testingFunction: (int)anNum{
    // if anNum < 2 -> the app will crash
    // and the NSLog statement will not execute
    // that mean you cannot see the string: "This statement will execute when anNum < 2"
    // into the log console window of Xcode
    NSAssert(anNum >= 2, @"number you enter less than 2");
    // If anNum >= 2 -> the app will not crash and the below 
    // statement will execute
    NSLog(@"This statement will execute when anNum < 2");
}

en mi código, la aplicación no se bloqueará. Y el caso de prueba es:

  • anNum > = 2 -> La aplicación no se bloqueará y puede ver la cadena de registro: "Esta instrucción se ejecutará cuando anNum <2" en la ventana de la consola de registro de salida
  • anNum <2 -> La aplicación se bloqueará y no podrá ver la cadena de registro: "Esta instrucción se ejecutará cuando anNum <2"

fuente
1
Lo tienes al revés. "NSAssert hace que la aplicación se bloquee cuando coincide con la condición. Si no coincide con la condición, se ejecutarán las siguientes declaraciones". NSAssert bloquea la aplicación si NO cumple con la condición y se ejecuta normalmente si SÍ coincide con la condición.
Joe Tam
La aplicación se bloquea y envía un mensaje de registro cuando no cumple con la condición; de lo contrario, se ejecuta más.
Pravin S.