Quelles sont les meilleures pratiques pour les exceptions / renvoyer NO / nil dans Objective-C?
Je suis nouveau à Objective-C, et je vois qu'il existe différentes conventions utilisées sur la gestion des erreurs. Il y a des exceptions, mais il y a aussi des situations où les fonctions sont censées renvoyer nil en cas de problème.
Alors, comment puis-je décider quand utiliser lequel, et comment gérer les exceptions et les valeurs de retour inattendues? Quelles sont les meilleures pratiques et les drapeaux rouges?
4 réponses
Je ne serai pas définitif sur lequel utiliser, mais voici quelques informations sur chacune des options:
Exceptions
Les Exceptions dans Obj-C ne sont pas vraiment destinées à être utilisées pour contrôler le flux de programme. De la documentation sur la gestion des exceptions :
Le modèle général est que les exceptions sont réservées aux erreurs de programmeur seulement, et le programme attrapant une telle exception devrait quitter peu de temps après.
Pour cette raison, Je ne voudrais pas recommander l'utilisation d'exceptions @try
/@catch
juste pour tester si une méthode a fonctionné correctement.
Vous avez également plusieurs options pour gérer les exceptions , en plus de définir un gestionnaire d'exceptions non intercepté de niveau supérieur.
Erreurs
Les erreurs sont généralement utilisées de trois façons:
Méthodes déléguées
Un objet peut simplement passer une NSError à son délégué dans un rappel de gestion des erreurs désigné:
- (void)myObject:(MyObject *)obj didFailWithError:(NSError *)error;
Le délégué est alors libre de prendre toute mesure appropriée, y compris peut-être l'affichage d'un message à l'utilisateur. Ce modèle est couramment utilisé dans les API asynchrones basées sur des délégués.
Paramètres de sortie
Ceux-ci sont le plus souvent utilisés en conjonction avec une valeur de retour booléenne: si la valeur de retour est NO
, alors L'objet NSError peut être examiné pour plus d'informations sur l'erreur.
- (BOOL)performTaskWithParameter:(id)param returningError:(out NSError **)error;
Où un modèle d'utilisation possible serait:
NSError *error;
if (![myObject performTaskWithParameter:@"param" returningError:&error]) {
NSLog(@"Task failed with error: %@", error);
}
(certaines personnes préfèrent également stocker le résultat booléen dans une variable avant de la Vérifier, comme BOOL success = [myObject perform...];
.) En raison de la nature linéaire de ce modèle, il est préférable d'utiliser pour les tâches synchrones.
Gestionnaires d'achèvement basés sur des blocs
Un modèle assez récent depuis l'introduction des blocs, mais assez utile:
- (void)performAsynchronousTaskWithCompletionHandler:(void (^)(BOOL success, NSError *error))handler;
Utilisé comme ceci:
[myObject performAsynchronousTaskWithCompletionHandler:^(BOOL success, NSError *error) {
if (!success) {
// ...
}
}];
Cela varie beaucoup: parfois vous ne verrez pas le paramètre booléen, juste l'erreur; parfois le bloc de gestionnaire n'a pas d'arguments qui lui sont passés et vous Vérifiez une propriété d'état de l'objet (par exemple, voici comment fonctionne AVAssetExportSession). Ce modèle est également idéal pour les tâches asynchrones, lorsque vous voulez une approche basée sur des blocs.
Gestion des erreurs
Cocoa sur Mac OS X a un chemin de gestion des erreurs assez complet . Il y a aussi la méthode de commodité de NSAlert + (NSAlert *)alertWithError:(NSError *)error;
. Sur iOS, la classe NSError existe toujours, mais il n'y a pas les mêmes méthodes pratiques pour gérer les erreurs. Vous pourriez avoir à faire beaucoup vous-même.
Lire l' Erreur de Manipulation Guide de Programmation pour plus d'informations.
Retour nil
Ceci est souvent utilisé en conjonction avec les paramètres NSError out; par exemple, la méthode de NSData
+ (id)dataWithContentsOfFile:(NSString *)path
options:(NSDataReadingOptions)mask
error:(NSError **)errorPtr;
Si la lecture du fichier échoue, cette méthode renvoie nil
, et d'autres informations sont stockées dans une erreur.
Une des raisons pour lesquelles ce modèle est particulièrement pratique est à cause de Nil messaging , qui peut être fait en toute sécurité sans effet dans Obj-C. Je n'entrerai pas dans les détails ici sur pourquoi cela est utile, mais vous pouvez en lire plus à ce sujet ailleurs sur les interwebs. (Assurez-vous simplement de trouver un article à jour; auparavant, les méthodes renvoyant des valeurs à virgule flottante ne retourneraient pas nécessairement 0 lorsqu'elles étaient envoyées à nil, mais maintenant elles le font, comme décrit dans la documentation.)
Les Exceptions doivent être utilisées le moins possible dans Objective-C. Là où d'autres langages utiliseraient des exceptions, dans Objective-C, il est recommandé d'utiliser des objets NSError la plupart du temps.
La documentation D'Apple sur la gestion des exceptions est ici: http://developer.apple.com/library/mac/#documentation/cocoa/conceptual/Exceptions/Exceptions.html%23//apple_ref/doc/uid/10000012il
Alors, comment utiliserait-on les objets NSError? Eh bien, si nous regardons les classes D'Apple, les erreurs sont renvoyées à l'aide d'un pointeur d'indirection.
Par exemple:
- (NSObject *)objectFromSet:(NSSet *)set error:(NSError **)error
{
// get an object from a set; if the set has at least 1 object
// we return an object, otherwise an error is returned.
NSObject *object = [set anyObject]
if (!object)
{
*error = [NSError errorWithDomain:@"AppDomain" code:1000 userInfo:nil];
return nil;
}
return object;
}
// and then we use the function like this
- (void)test
{
NSError *error = nil;
NSSet *set = [[[NSSet alloc] init] autorelease];
NSObject *object = [self objectFromSet:set error:&error];
if (error)
{
// handle error, perhaps show an alert view ...
}
else
{
// use the object, all went fine ...
}
}
Objective-C supporte les exceptions de la même manière que les autres langages de programmation, avec une syntaxe similaire à Java ou c++. Comme avec NSError
, les exceptions dans Cocoa et Cocoa Touch sont des objets, représentés par des instances de la classe NSException,
Vous pouvez utiliser
@try {
// do something that might throw an exception
}
@catch (NSException *exception) {
// deal with the exception
}
@finally {
// optional block of clean-up code
// executed whether or not an exception occurred
}
Afficher plus sur la gestion des erreurs Apple doc .
Si une méthode est supposée renvoyer un objet, et qu'elle est incapable de le faire, elle devrait renvoyer nil. S'il y a une erreur que vous souhaitez signaler à l'utilisateur afin qu'il puisse prendre une sorte d'action à ce sujet, utilisez un objet NSError.