web-dev-qa-db-fra.com

jeter une exception dans objective-c / cacao

Quelle est la meilleure façon de lancer une exception dans objective-c/cacao?

409
Steph Thirion

J'utilise [NSException raise:format:] comme suit:

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

Un mot de prudence ici. En Objective-C, contrairement à de nombreux langages similaires, vous devriez généralement éviter d'utiliser des exceptions pour les situations d'erreur courantes pouvant survenir en mode de fonctionnement normal.

La documentation d’Apple pour Obj-C 2. indique ce qui suit: "Important: Les exceptions nécessitent beaucoup de ressources dans Objective-C. Vous ne devez pas utiliser les exceptions pour le contrôle de flux général, ou simplement pour signaler des erreurs (telles que un fichier non accessible) "

La documentation de gestion des exceptions conceptuelles d’Apple explique la même chose, mais avec plus de mots: "Important: vous devez réserver l’utilisation des exceptions à la programmation ou des erreurs d’exécution inattendues telles que l’accès hors collection à la collection, les tentatives de mutation. objets immuables, envoi d'un message non valide et perte de la connexion au serveur de fenêtres.Vous vous chargez généralement de ce type d'erreur avec des exceptions lors de la création d'une application plutôt qu'à l'exécution. [.....] Au lieu d'exceptions, Les objets d'erreur (NSError) et le mécanisme de livraison des erreurs Cocoa sont le moyen recommandé pour communiquer les erreurs attendues dans les applications Cocoa. "

Cela s’explique en partie par l’adhésion aux idiomes de programmation d’Objective-C (utilisation de valeurs de retour dans des cas simples et de paramètres de référence (souvent la classe NSError) dans des cas plus complexes), en partie du fait que lancer et intercepter des exceptions est beaucoup plus coûteux et enfin (et surtout, les erreurs) que les exceptions Objective-C sont une mince couche autour des fonctions setjmp () et longjmp () de C, gâchant essentiellement votre gestion minutieuse de la mémoire, voir cette explication .

256
harms
@throw([NSException exceptionWith…])
60
Peter Hosey

Je n'ai pas le représentant à commenter sur la réponse de eJames, donc je suppose que je dois mettre la mienne ici. Pour ceux qui proviennent d'un arrière-plan Java, vous vous rappellerez que Java distingue Exception de RuntimeException. L'exception est une exception cochée et RuntimeException n'est pas cochée. En particulier, Java suggère d'utiliser les exceptions vérifiées pour les "conditions d'erreur normales" et les exceptions non vérifiées pour les "erreurs d'exécution causées par une erreur du programmeur". Il semble que les exceptions Objective-C doivent être utilisées aux mêmes endroits que les exceptions non vérifiées, et que les valeurs de retour de code d'erreur ou les valeurs NSError sont préférées aux endroits où vous utiliseriez une exception vérifiée.

33
Daniel Yankowsky

Je pense que pour être cohérent, il est préférable d'utiliser @throw avec votre propre classe qui étend NSException. Ensuite, vous utilisez les mêmes notations pour essayer attraper, enfin:

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

Apple explique ici comment lancer et gérer des exceptions: Catching ExceptionsLancer des exceptions

15
rustyshelf

Depuis ObjC 2.0, les exceptions Objective-C ne sont plus un wrapper pour setjmp () longjmp () du C, et sont compatibles avec l'exception C++, le @try est "gratuit", mais lancer et intercepter des exceptions est beaucoup plus coûteux.

Quoi qu’il en soit, les assertions (utilisant la famille de macros NSAssert et NSCAssert) lancent NSException, et cela n’a aucun sens de les utiliser comme États de Ries.

14
Psycho

Utilisez NSError pour communiquer des échecs plutôt que des exceptions.

Quelques points à propos de NSError:

  • NSError permet aux codes d'erreur de style C (entiers) d'identifier clairement la cause première et, espérons-le, de permettre au gestionnaire d'erreurs de remédier à l'erreur. Vous pouvez très facilement insérer des codes d'erreur à partir de bibliothèques C telles que SQLite dans des instances de NSError.

  • NSError a également l'avantage d'être un objet et offre un moyen de décrire l'erreur plus en détail avec son membre du dictionnaire userInfo.

  • Mais le meilleur de tous, NSError NE PEUT PAS être jeté, il encourage donc une approche plus proactive de la gestion des erreurs, contrairement à d'autres langages qui jettent simplement la patate chaude de plus en plus loin dans la pile d'appels, auquel cas elle ne peut être rapportée qu'à l'utilisateur. pas traité de manière significative (pas si vous croyez à suivre le plus gros principe de dissimulation d'informations d'OP):.

Lien de référence: référence

8
Jason Fuerstenberg

Voici comment je l'ai appris de "The Big Nerd Ranch Guide (4th edition)":

@throw [NSException exceptionWithName:@"Something is not right exception"
                               reason:@"Can't perform this operation because of this or that"
                             userInfo:nil];
7
Johannes

Vous pouvez utiliser deux méthodes pour lever une exception dans le bloc try catch

@throw[NSException exceptionWithName];

ou la deuxième méthode

NSException e;
[e raise];
6
Subbu

Je crois que vous ne devriez jamais utiliser Exceptions pour contrôler le déroulement normal du programme. Mais des exceptions doivent être levées chaque fois qu'une valeur ne correspond pas à une valeur souhaitée.

Par exemple, si une fonction accepte une valeur, et que cette valeur ne doit jamais être nulle, il est normal de générer une exception plutôt que d'essayer de faire quelque chose de "intelligent" ...

Ries

3
R. van Twisk

Exemple de code pour le cas: @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);
}

}

En utilisant:

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

Un autre cas d'utilisation plus avancé:

- (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

}

}

0
Aleksandr B.

Vous ne devriez lancer des exceptions que si vous vous trouvez dans une situation qui indique une erreur de programmation et souhaitez arrêter l'exécution de l'application. Par conséquent, le meilleur moyen de générer des exceptions consiste à utiliser les macros NSAssert et NSParameterAssert et à vous assurer que NS_BLOCK_ASSERTIONS n'est pas défini.

0
gnasher729