levant une exception en objectif-c / cacao

Réponses:

528

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

[NSException raise:@"Invalid foo value" format:@"foo of %d is invalid", foo];
e.James
la source
9
Je préfère cette façon d'apposer à l' @throw([NSException exceptionWith…])approche car elle est plus concise.
Sam Soffes
9
Assurez-vous de lire la mise en garde importante des dommages ( stackoverflow.com/questions/324284/324805#324805 )
e.James
26
Je préfère généralement cela aussi, mais il y a un problème. Peut-être juste ma version actuelle de Xcode, mais la syntaxe [NSException raise ...] ne semble pas être reconnue par l'analyseur comme un chemin de sortie d'une méthode qui retourne une valeur. Je vois l'avertissement «Le contrôle peut atteindre la fin de la fonction non vide» lors de l'utilisation de cette syntaxe, mais avec la syntaxe @throw ([NSException exceptionWith…]), l'analyseur reconnaît cela comme une sortie et n'affiche pas l'avertissement.
mattorb
1
@mpstx J'utilise toujours la syntaxe throw pour la raison que vous avez donnée (qui est toujours pertinente deux ans plus tard dans Xcode 4.6, et le sera probablement toujours). Faire en sorte que l'EDI reconnaisse que lever une exception est un point de sortie de fonction compte souvent si vous voulez éviter les avertissements.
Mark Amery
FWIW Je remarque que les blocs @ try / @ catch entraînent également un faux négatif pour les avertissements "le contrôle atteint la fin de la fonction non nulle" (c'est-à-dire que l'avertissement n'est pas affiché quand il devrait l'être)
Brian Gerstle
256

Un mot de prudence ici. Dans Objective-C, contrairement à de nombreux langages similaires, vous devez généralement éviter d'utiliser des exceptions pour les situations d'erreur courantes qui peuvent se produire en fonctionnement normal.

Documentation d'Apple pour Obj-C 2.0 stipule ce qui suit: "Important: Les exceptions sont gourmandes en ressources dans Objective-C. Vous ne devez pas utiliser d'exceptions pour le contrôle de flux général, ou simplement pour signifier des erreurs (comme un fichier non accessible)"

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 d'exceptions pour la programmation ou les erreurs d'exécution inattendues telles que l'accès à la collection hors limites, les tentatives de mutation d'objets immuables, l'envoi d'un message non valide et la perte de la connexion au serveur Windows. En règle générale, vous vous occupez de ce type d'erreurs avec des exceptions lors de la création d'une application plutôt qu'au moment de l'exécution. [.....] Au lieu d'exceptions, les objets d'erreur (NSError) et le Le mécanisme de remise des erreurs Cocoa est le moyen recommandé de communiquer les erreurs attendues dans les applications Cocoa. "

Cela s'explique en partie par le respect des idiomes de programmation dans Objective-C (en utilisant des valeurs de retour dans des cas simples et des paramètres de référence (souvent la classe NSError) dans des cas plus complexes), en partie parce que lever et intercepter des exceptions est beaucoup plus cher et enfin (et perplexe le plus important) que les exceptions Objective-C sont un wrapper mince autour des fonctions setjmp () et longjmp () de C, gâchant essentiellement votre gestion prudente de la mémoire, voir cette explication .

nuit
la source
11
Je pense que cela s'applique à la plupart des langages de programmation: "essayez d'éviter d'utiliser des exceptions pour les situations d'erreur courantes". La même chose s'applique en Java; c'est une mauvaise pratique de gérer les erreurs de saisie utilisateur (par exemple) avec des exceptions. Non seulement à cause de l'utilisation des ressources, mais aussi pour la clarté du code.
beetstra
6
Plus important encore, les exceptions dans Cocoa sont conçues pour indiquer des erreurs de programme non récupérables. Sinon, cela va à l'encontre du framework et peut conduire à un comportement indéfini. Voir stackoverflow.com/questions/3378696/iphone-try-end-try/… pour plus de détails.
KPM
9
"La même chose s'applique en Java;" Être en désaccord. Vous pouvez très bien utiliser les exceptions vérifiées en Java pour des conditions d'erreur normales. Bien sûr, vous n'utiliseriez pas d'exceptions d'exécution.
Daniel Ryan
Je préfère la méthode Cocoa (les exceptions concernent uniquement les erreurs de programmation), je préférerais donc le faire également en Java, mais la réalité est que vous devriez suivre des pratiques typiques dans un environnement, et les exceptions pour la gestion des erreurs ressortent comme un mauvaise odeur dans Objective-C, mais sont beaucoup utilisées à cet effet en Java.
gnasher729
1
Ce commentaire ne répond pas à la question. Peut-être que l'OP veut juste planter l'application pour tester si le framework de rapport de crash fonctionne comme prévu.
Simon
62
@throw([NSException exceptionWith…])

Xcode reconnaît les @throwinstructions comme des points de sortie de fonction, comme les returninstructions. L'utilisation de la @throwsyntaxe évite les avertissements erronés "Le contrôle peut atteindre la fin de la fonction non nulle " que vous pouvez obtenir [NSException raise:…].

En outre, @throwpeut être utilisé pour lancer des objets qui ne sont pas de NSException de classe.

Peter Hosey
la source
11
@Steph Thirion: Voir developer.apple.com/documentation/Cocoa/Conceptual/Exceptions/… pour tous les détails. En bout de ligne? Les deux fonctionneront, mais @throw peut être utilisé pour lancer des objets qui ne sont pas de la classe NSException.
e.James
33

Concernant [NSException raise:format:]. Pour ceux qui viennent d'un arrière-plan Java, vous vous souviendrez que Java fait la distinction entre Exception et RuntimeException. L'exception est une exception vérifiée et RuntimeException n'est pas cochée. En particulier, Java suggère d'utiliser des exceptions vérifiées pour les "conditions d'erreur normales" et des exceptions non vérifiées pour les "erreurs d'exécution causées par une erreur de programmation". Il semble que les exceptions Objective-C devraient être utilisées aux mêmes endroits que vous utiliseriez une exception non vérifiée, et 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.

Daniel Yankowsky
la source
1
Oui, c'est correct (après 4 ans cependant: D), créez votre propre classe d'erreur ABCError qui s'étend de la classe NSError et utilisez-la pour les exceptions vérifiées plutôt que NSExceptions. Élevez des exceptions NSE où les erreurs de programmation (situation inattendue comme un problème de format numérique) se produisent.
chathuram
15

Je pense que pour être cohérent, il est plus agréable d'utiliser @throw avec votre propre classe qui étend NSException. Ensuite, vous utilisez les mêmes notations pour essayer finalement catch:

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

Apple explique ici comment lever et gérer les exceptions: Attraper les exceptions Lancer les exceptions

rustyshelf
la source
j'ai toujours eu un crash par exception d'exécution dans le bloc try
famfamfam
14

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

Quoi qu'il en soit, les assertions (en utilisant NSAssert et la famille de macros NSCAssert) lèvent NSException, et cela est sensé de les utiliser comme états Ries.

Psycho
la source
Bon à savoir! Nous avons une bibliothèque tierce que nous ne voulons pas modifier qui lève des exceptions, même pour les plus petites erreurs. Nous devons les attraper au même endroit dans l'application et cela nous fait grincer des dents, mais cela me fait me sentir un peu mieux.
Yuri Brigance
8

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

Points rapides sur NSError:

  • NSError permet aux codes d'erreur de style C (entiers) d'identifier clairement la cause racine et, espérons-le, au gestionnaire d'erreurs de surmonter l'erreur. Vous pouvez très facilement encapsuler les codes d'erreur des bibliothèques C comme SQLite dans les instances 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 de dictionnaire userInfo.

  • Mais le meilleur de tous, NSError NE PEUT PAS être lancé, ce qui encourage une approche plus proactive de la gestion des erreurs, contrairement à d'autres langues qui jettent simplement la patate chaude de plus en plus haut dans la pile d'appels, auquel cas elle ne peut être signalée qu'à l'utilisateur et pas traité de manière significative (pas si vous croyez qu'il faut respecter le plus grand principe de la POO en matière d'information qui se cache).

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

Jason Fuerstenberg
la source
Ce commentaire ne répond pas à la question. Peut-être que l'OP veut juste planter l'application pour tester si le framework de rapport de crash fonctionne comme prévu.
Simon
7

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];
Johannes
la source
D'accord, mais cela ne dit pas grand-chose sur le userInfo:nil. :)
Cœur
6

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

@throw[NSException exceptionWithName];

ou la deuxième méthode

NSException e;
[e raise];
Subbu
la source
3

Je pense que vous ne devez jamais utiliser d'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 n'est jamais autorisée à être nulle, alors c'est bien de trow une exception plutôt que d'essayer de faire quelque chose de 'intelligent' ...

Ries

R. van Twisk
la source
0

Vous ne devez lever des exceptions que si vous vous trouvez dans une situation qui indique une erreur de programmation et que vous souhaitez arrêter l'application en cours d'exécution. Par conséquent, la meilleure façon de lever des exceptions est d'utiliser les macros NSAssert et NSParameterAssert et de vous assurer que NS_BLOCK_ASSERTIONS n'est pas défini.

gnasher729
la source
0

Exemple de code pour la casse: @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

}

}

Aleksandr B.
la source
-7

Il n'y a aucune raison de ne pas utiliser les exceptions normalement dans l'objectif C même pour signifier des exceptions aux règles métier. Apple peut dire utiliser NSError qui s'en soucie. Obj C existe depuis longtemps et à un moment donné, TOUTE la documentation C ++ a dit la même chose. La raison pour laquelle le fait de lancer et d'attraper une exception coûte peu est que la durée de vie d'une exception est extrêmement courte et ... c'est une EXCEPTION pour le flux normal. Je n'ai jamais entendu personne dire quoi que ce soit de ma vie, l'homme que cette exception a mis longtemps à être levée et rattrapée.

En outre, il y a des gens qui pensent que l'objectif C lui-même est trop cher et code à la place en C ou C ++. Donc, dire toujours utiliser NSError est mal informé et paranoïaque.

Mais la question de ce fil n'a pas encore été répondue quelle est la meilleure façon de lever une exception. Les moyens de retourner NSError sont évidents.

Il en est de même: [NSException raise: ... @throw [[NSException alloc] initWithName .... ou @throw [[MyCustomException ...?

J'utilise ici la règle cochée / décochée légèrement différemment que ci-dessus.

La vraie différence entre (en utilisant la métaphore java ici) cochée / décochée est importante -> si vous pouvez récupérer de l'exception. Et par récupérer, je veux dire non seulement PAS planter.

J'utilise donc des classes d'exceptions personnalisées avec @throw pour les exceptions récupérables, car il est probable que j'aurai une méthode d'application recherchant certains types d'échecs dans plusieurs blocs @catch. Par exemple, si mon application est un guichet automatique, j'aurais un bloc @catch pour la "WithdrawalRequestExceedsBalanceException".

J'utilise NSException: lever pour les exceptions d'exécution car je n'ai aucun moyen de récupérer à partir de l'exception, sauf pour l'attraper à un niveau supérieur et l'enregistrer. Et il n'y a aucun intérêt à créer une classe personnalisée pour cela.

Quoi qu'il en soit, c'est ce que je fais, mais s'il y a une meilleure manière expressive, j'aimerais aussi savoir. Dans mon propre code, depuis que j'ai arrêté de coder C il y a longtemps, je ne retourne jamais de NSError même si je suis passé par une API.

utilisateur supprimé
la source
4
Je recommanderais d'essayer de programmer un serveur avec des exceptions dans le cadre du flux normal de cas d'erreur avant de faire des déclarations généralisantes comme "il n'y a aucune raison de ne pas utiliser les exceptions normalement dans l'objectif C". Croyez-le ou non, il existe des raisons d'écrire des applications hautes performances (ou au moins des parties d'applications) dans ObjC, et le fait de lever des exceptions entrave normalement sérieusement les performances.
jbenet
6
Il y a en effet de très bonnes raisons de ne pas utiliser d'exceptions dans Cocoa. Voir la réponse de Bill Bumgarner ici pour en savoir plus: stackoverflow.com/questions/3378696/iphone-try-end-try/… . Il sait de quoi il parle (indice: vérifiez son employeur). Les exceptions dans Cocoa sont traitées comme des erreurs irrécupérables et peuvent laisser le système dans un état instable. NSError est la voie à suivre pour contourner les erreurs générales.
Brad Larson
Les exceptions sont exceptionnelles . Les échecs des règles commerciales ne sont certainement pas admissibles. "La recherche et la conception d'un code d'exception peuvent entraîner une victoire de performance décente." MSDN via codinghorror.com/blog/2004/10/…
Jonathan Watmough
3
Les exceptions ne peuvent pas être levées des blocs. Les exceptions levées dans un environnement ARC pourraient faire fuir votre programme. D'où le downvote.
Moszi
"Peu importe le coût de lancer et de capturer une exception", j'écris un émulateur où les performances sont essentielles. Je ne peux pas jeter un tas d'exceptions coûteuses.
NobodyNada