2010-11-30 9 views
8

Un de mes amis m'a demandé de ne pas utiliser NSException dans les applications iPhone. La raison qu'il a donnée était "goulot d'étranglement de la performance". Mais je n'en suis pas convaincu. Est-ce que quelqu'un peut me confirmer que nous devrions restreindre l'utilisation de NSException dans l'application iPhone? Si vous avez des bonnes pratiques pour utiliser NSException, veuillez le fournir également.Utilisation de NSException dans les applications iPhone

MISE À JOUR:

Ce link nous demandent d'utiliser la gestion des exceptions au niveau de l'application. Est-ce que quelqu'un l'a déjà fait? S'il vous plaît donner les avantages de celui-ci et tous les autres problèmes de performance qu'il pourrait créer.

Répondre

30

En bref:

Ne pas utiliser des exceptions pour indiquer quoi que ce soit, mais des erreurs irrécupérables

Il est seulement approprié d'utiliser @ try/catch @ pour faire face à des erreurs irrécupérables. Il n'est jamais approprié d'utiliser @ throw/@ try/@ catch pour effectuer des opérations comme sur iOS ou Mac OS X. Même dans ce cas, réfléchissez bien pour savoir s'il vaut mieux utiliser une exception pour indiquer une erreur irrécupérable ou simplement se bloquer (appel abandon()); un accident laisse souvent derrière lui beaucoup plus de preuves. Par exemple, il ne serait pas approprié d'utiliser pour attraper des exceptions hors limites à moins que votre but soit de les attraper et de signaler l'erreur, puis - typiquement - de planter ou, à tout le moins, d'avertir le utilisateur que votre application est dans un état incohérent et peut perdre des données.

Le comportement de toute exception lancée via le code de structure du système n'est pas défini.


Pouvez-vous expliquer le "comportement de toute exception lancée par le système code cadre est défini." en détail?

Bien sûr.

Les infrastructures système utilisent une conception dans laquelle toute exception est considérée comme une erreur irrécupérable irrécupérable; une erreur de programmeur, à toutes fins utiles. Il y a un nombre très limité d'exceptions (heh) à cette règle. Par conséquent, dans leur mise en œuvre, les cadres du système ne garantiront pas que tout est correctement nettoyé si une exception est lancée qui traverse le code de structure du système. Sine l'exception est, par définition, irrécupérable, pourquoi payer le coût du nettoyage?

cette pile Tenir compte appel:

your-code-1() 
    system-code() 
     your-code-2() 

à savoir code où votre code appelle dans le code système qui appelle plus de votre code (un modèle très commun, bien que les piles d'appels sont évidemment beaucoup plus profondes).

Si your-code-2 déclenche une exception, que les exceptions passent sur system-code signifie que le comportement est indéfini; system-code peut ou non laisser votre application dans un état indéfini, potentiellement en panne ou avec une perte de données.

Ou, plus fortement: Vous ne pouvez pas lever une exception dans your-code-2 avec l'espoir que vous pouvez l'attraper et le gérer dans your-code-1.

+0

Pouvez-vous expliquer le "Comportement de toute exception levée par le biais du code de structure du système est indéfini." en détail? – Krishnan

+0

Mis à jour la question .... – Krishnan

+0

+1, il est gentil one.Peut-on me dire s'il vous plaît mon hypothèse concernant l'exception n'est pas correct.Si j'utilise NSException en cas d'exception particulière (supposons Array hors de la limite) .then charge toutes les classes pour vérifier l'exception. Veuillez répondre. – Ishu

0

En général, demandez-vous si vous essayez de signaler une erreur ou si vous avez réellement une condition exceptionnelle. Si le premier, alors c'est une très mauvaise idée indépendamment de tout problème de performance, réel ou perçu. Si ce dernier, c'est certainement la bonne chose à faire.

+2

Pas vraiment; Les exceptions dans iOS concernent les erreurs irrécupérables. – bbum

+1

Pour moi, les situations récupérables sont des erreurs :) – jer

6

J'ai utilisé la gestion des exceptions pour mon audio app assez intensif sans aucun problème. Après beaucoup de lecture et un peu d'analyse comparative et de désassemblage, j'en suis arrivé à la conclusion controversée qu'il n'y a pas de raison de ne pas les utiliser (intelligemment) et beaucoup de raisons de ne pas les utiliser (pointeurs NSError ... beurk!). La plupart de ce que les gens disent sur les forums ne fait que répéter les Apple Docs.

je vais dans un peu de détails dans this blog post mais je vais décrire mes résultats ici:

Mythe 1: @ essayer/@ catch/@ est finalement trop cher (en termes de CPU)

Sur mon iPhone 4, lancer et attraper 1 million d'exceptions prend environ 8,5 secondes. Cela équivaut à environ seulement 8,5 microsecondes pour chacun. Cher dans votre thread CoreAudio en temps réel? Peut-être un peu (mais vous ne jetteriez jamais d'exceptions là-bas ??), mais un retard de 8.5μs dans l'UIAlert indiquant à l'utilisateur qu'il y avait un problème à ouvrir son fichier, cela va-t-il être remarqué?

Mythe 2: Les blocs de @try un coût sur iOS 32bit

Les documents d'Apple parlent de « blocs de @try à coût zéro sur 64bit » et que l'état 32 bits entraîne un coût. Une petite analyse de benchmarking et de désassemblage semble indiquer qu'il existe également des blocs @try à coût nul sur iOS 32 bits (processeur ARM). Apple a-t-il voulu dire 32 bits Intel?

Mythe n ° 3: Il importe que des exceptions Jeté à travers des cadres de cacao sont Undefined

Oui, ils sont « non défini », mais que faites-vous jeter dans le cadre d'Apple de toute façon? Bien sûr Apple ne les gère pas pour vous. L'objectif de la gestion des exceptions pour les erreurs récupérables est de les gérer localement, mais pas toutes les lignes "localement".

Un cas de bord est ici avec des méthodes comme NSObject:performSelectorOnMainThread:waitUntilDone:. Si le paramètre suivant est YES, cela agit comme une fonction synchrone, auquel cas vous pourriez être pardonné d'attendre que l'exception atteigne la portée de votre appel. Par exemple:

///////////////////////////////////////////////////////////////////////// 
#pragma mark - l5CCThread 
///////////////////////////////////////////////////////////////////////// 

@interface l5CCThread : NSThread @end 

@implementation l5CCThread 

- (void)main 
{ 
    @try { 

     [self performSelectorOnMainThread:@selector(_throwsAnException) withObject:nil waitUntilDone:YES]; 

    } @catch (NSException *e) { 
     NSLog(@"Exception caught!"); 
    } 
} 
- (void)_throwsAnException { @throw [NSException exceptionWithName:@"Exception" reason:@"" userInfo:nil]; } 

@end 

///////////////////////////////////////////////////////////////////////// 
#pragma mark - l5CCAppDelegate 
///////////////////////////////////////////////////////////////////////// 

@implementation l5CCAppDelegate 

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 
{ 
    l5CCThread *thd = [[l5CCThread alloc] init]; 
    [thd start]; 

    return YES; 
} 
// ... 

Dans ce cas, l'exception passerait « dans le cadre de cacao » (la boucle d'exécution du thread principal) manquant vos prises et s'écraser. Vous pouvez facilement contourner cela en utilisant dispatch_synch de GCD et en mettant dans son argument de bloc l'appel de méthode plus n'importe quelle gestion d'exception.

Pourquoi utiliser NSException de plus de

NSError

de Quiconque a déjà fait un travail dans l'un des cadres basés C-anciens comme Core Audio sait ce qu'est une corvée, il vérifie, la manipulation et les erreurs de déclaration. Le principal avantage de @ try/@ catch et NSExceptions est de rendre votre code plus propre et plus facile à maintenir.

Disons que vous avez 5 lignes de code qui fonctionnent sur un fichier. Chacun d'entre eux peut, par exemple, lancer 3 erreurs différentes (par exemple, sur l'espace disque, erreur de lecture, etc.). Au lieu d'encapsuler chaque ligne dans un conditionnel qui vérifie une valeur de retour NO et externalise l'investigation du pointeur NSError vers une autre méthode ObjC (ou pire, utilise une macro #define!), Vous enroulez toutes les lignes un seul @try et gérer chaque erreur là-bas. Pensez aux lignes que vous économiserez! En créant des sous-classes NSException, vous pouvez également centraliser facilement les messages d'erreur et éviter que votre code ne soit jonché de détritus. Vous pouvez également distinguer facilement les exceptions «non fatales» de votre application des erreurs programmatiques fatales (comme NSAssert). Vous pouvez également éviter la nécessité de la constante "nom" (le nom de la sous-classe, est le "nom").

Des exemples de tout cela et plus de détails sur les points de référence et le démontage sont on this blog post ...

Exceptions, plus try/catch/est finalement le paradigme utilisé par à peu près toutes les autres langues principales (C++, Java, PHP, Ruby, Python). Peut-être qu'il est temps de laisser tomber la paranoïa et de l'adopter aussi ... au moins dans iOS.

+6

Suggestion d'utilisation correcte de NSExceptions pour les erreurs récupérables et le contrôle de flux est incorrecte. Les frameworks ne le supportent pas explicitement. – bbum

+0

Mis à part le point (plus important) que fait bbum, j'ai du mal à voir comment try/catch a amélioré la ligibilité: si tout ce que vous voulez n'est pas récupérer, revenez tôt et souvent. Si vous avez une cascade de captures, comment ça va mieux? – danyowdee

+2

Parce que vous centralisez la gestion de chaque type d'erreur à un seul endroit pour un bloc de code donné plutôt que par ligne. –

Questions connexes