objective-c excepciones - Lanzar una excepción en objetivo-c / cacao.




para getmessage (12)

@throw([NSException exceptionWith…])

¿Cuál es la mejor manera de lanzar una excepción en object-c / cacao?


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 indica lo siguiente: "Importante: las excepciones requieren un uso intensivo de recursos en Objective-C. No debe usar excepciones para el control de flujo general, o simplemente para indicar errores (como que no se pueda acceder a un archivo)"

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 en el tiempo de ejecución, como el acceso a colecciones fuera de los límites, intentos de mutar objetos inmutables, enviar un mensaje no válido. , y perdiendo la conexión con el servidor de Windows. Generalmente se hace cargo 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 Cocoa es la forma recomendada de comunicar los errores esperados en las aplicaciones de Cocoa ".

Las razones de esto son, en parte, adherirse a los lenguajes de programación en Objective-C (usar valores de retorno en casos simples y parámetros de referencia (a menudo la clase NSError) en casos más complejos), en parte porque lanzar y atrapar excepciones es mucho más costoso y finalmente (y señala, lo más importante) que las excepciones de Objective-C son una envoltura delgada alrededor de las funciones setjmp () y longjmp () de C, esencialmente estropeando el manejo cuidadoso de la memoria; consulte esta explicación .


No hay razón para no usar excepciones normalmente en el objetivo C, incluso para indicar excepciones de reglas de negocios. Apple puede decir usar a NSError a quién le importa. Obj C ha existido por mucho tiempo y, al mismo tiempo, TODA la documentación de C ++ decía lo mismo. La razón por la que no importa lo costoso que sea 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 escuchado a nadie decir en mi vida, hombre, esa excepción tardó mucho tiempo en ser lanzada y atrapada.

Además, hay personas que piensan que el objetivo C en sí es demasiado caro y el código en C o C ++ en su lugar. Así que decir que usar siempre NSError está mal informado y es paranoico.

Pero la pregunta de este hilo aún no ha sido respondida cuál es la MEJOR manera de lanzar una excepción. Las formas de devolver NSError son obvias.

Así es: [NSException raise: ... @throw [[NSException alloc] initWithName .... o @throw [[MyCustomException ...?

Yo uso la regla marcada / no marcada aquí de forma ligeramente diferente a la anterior.

La diferencia real entre (usando la metáfora java aquí) marcada / no marcada es importante -> si puede recuperarse de la excepción. Y por recuperar quiero decir no solo NO chocar.

Así que uso clases de excepción personalizadas con @throw para excepciones recuperables, porque es probable que tenga algún método de aplicación buscando ciertos tipos de fallas en varios bloques de @catch. Por ejemplo, si mi aplicación es un cajero automático, tendría un bloque @catch para "WithdrawalRequestExceedBalanceException".

Utilizo NSException: aumento para excepciones en 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 manera mejor, similar expresiva, me gustaría saber también. En mi propio código, desde que dejé de codificar C a hella hace mucho tiempo, nunca devuelvo un NSError incluso si una API me pasa uno.


Utilizo [NSException raise:format:] siguiente manera:

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

Creo que para ser consistente es mejor usar @throw con tu propia clase que extiende NSException. Luego usas las mismas notaciones para intentar atrapar finalmente:

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

Apple explica aquí cómo lanzar y manejar excepciones: cómo atrapar excepciones Cómo lanzar developer.apple.com/documentation/Cocoa/Conceptual/Exceptions/…


Código de ejemplo para el caso: @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:@"<Set Yours exc. name: > Test Exc" reason:@"<Describe 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);
}

}

Utilizando:

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

Otro caso de uso más avanzado:

- (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:@"<Set Yours exc. name: > Test Exc" reason:@"<Describe 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

}

}


No tengo el representante para comentar la respuesta de eJames, así que supongo que necesito poner la mía aquí. Para aquellos que vienen de un fondo de Java, recordará que Java distingue entre Exception y RuntimeException. La excepción es una excepción marcada, y RuntimeException no está marcada. En particular, Java sugiere utilizar excepciones comprobadas para "condiciones de error normales" y excepciones no comprobadas para "errores de tiempo de ejecución causados ​​por un error de programador". Parece que las excepciones de Objective-C deberían usarse en los mismos lugares en los que usaría una excepción no verificada, y se prefieren los valores de retorno de código de error o los valores NSError en los lugares donde usaría una excepción marcada.


Creo que nunca debe usar Excepciones para controlar el flujo normal del programa. Pero deben lanzarse excepciones siempre que algún valor no coincida con un valor deseado.

Por ejemplo, si alguna función acepta un valor, y ese valor nunca puede ser nulo, entonces está bien lanzar una excepción en lugar de intentar hacer algo 'inteligente' ...

Ries


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 lanzar excepciones es usar las macros NSAssert y NSParameterAssert, y asegurarse de que NS_BLOCK_ASSERTIONS no esté definido.


Dado que ObjC 2.0, las excepciones de Objective-C ya no son un envoltorio para setjmp () longjmp () de C, y son compatibles con la excepción de C ++, @try es "gratuito", pero las excepciones de lanzamiento y captura son mucho más caras.

De todos modos, las aserciones (utilizando la familia de macros NSAssert y NSCAssert) arrojan NSException, y eso puede usarlas como estados de Ries.


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 ajustar códigos de error de bibliotecas C como SQLite en instancias de NSError con mucha facilidad.

  • NSError también tiene la ventaja de ser un objeto y ofrece una manera de describir el error con más detalle con su miembro del diccionario UserInfo.

  • Pero lo mejor de todo es que NSError NO SE PUEDE lanzar, por lo que fomenta un enfoque más proactivo para el manejo de errores, en contraste con otros idiomas que simplemente lanzan la papa caliente más allá de la pila de llamadas, momento en el que solo se puede informar al usuario no se maneja de manera significativa (no si usted cree en seguir el principio más importante de OOP de la ocultación de la información).

Enlace de Reference : Reference


Lea primero las respuestas existentes, esto es solo un apéndice.

Tenga en cuenta que puede generar excepciones con o sin argumentos.

Ejemplo:

raise SystemExit

sale del programa pero es posible que desee saber qué sucedió. Así que puede usar esto.

raise SystemExit("program exited")

esto imprimirá "programa salido" a stderr antes de cerrar el programa.







objective-c cocoa exception-handling