Quel est l'intérêt de NSAssert, en fait?

155

Je dois poser cette question, car: La seule chose que je reconnais, c'est que si l'assertion échoue, l'application plante. Est-ce la raison pour laquelle utiliser NSAssert? Ou quel est l'avantage de celui-ci? Et est-il juste de mettre un NSAssert juste au-dessus de toute hypothèse que je fais dans le code, comme une fonction qui ne devrait jamais recevoir un -1 comme paramètre mais peut-être -0,9 ou -1,1?

TheNeil
la source

Réponses:

300

Assert, c'est s'assurer qu'une valeur est ce qu'elle est censée être. Si une assertion échoue, cela signifie que quelque chose s'est mal passé et que l'application se ferme. Une raison d'utiliser assert serait si vous avez une fonction qui ne se comportera pas ou qui créera de très mauvais effets secondaires si l'un des paramètres qui lui sont passés n'est pas exactement une valeur (ou une plage de valeurs), vous pouvez mettre une assertion à faire Assurez-vous que cette valeur est ce que vous attendez d'elle, et si ce n'est pas le cas, quelque chose ne va vraiment pas et l'application se ferme. Assert peut être très utile pour le débogage / les tests unitaires, et aussi lorsque vous fournissez des frameworks pour empêcher les utilisateurs de faire des choses «mauvaises».

Daniel
la source
9
Vous devez retirer NSAssert pour la libération. Il existe un indicateur de compilation pour ce faire.
Barry Wark
127
> Vous devez retirer NSAssert pour la libération. C'est discutable. Je publie toujours mes applications avec les assertions activées, et c'est une pratique courante pour beaucoup de logiciels, Apple par exemple le fait. Dès que votre programme a détecté un état anormal, vous devez planter. Vous pouvez obtenir une trace de la pile de l'endroit où l'erreur s'est produite, tandis que si vous désactivez les assertions, vous risquez de corrompre la mémoire et / ou les données utilisateur et le problème sera très difficile à déboguer.
Mike Weller
18
Notez que XCode 4 a NS_BLOCK_ASSERTIONS défini par défaut dans les configurations de version. Je suppose que si vous ne changez pas, votre code publié ne contiendra pas NSAssert: s.
Jonny
16
Si je comprends bien, quel est l'intérêt de les laisser (sur la version de sortie)? Pourquoi ne pas remplacer NSAssert par une instruction if, et si (quelque chose de terrible se produit), informez-en l'utilisateur (ou faites quelque chose qui est sous votre contrôle), et pas seulement quittez / plantez et laissez l'utilisateur se demander ce qui s'est passé ... Ou est-ce que je manque quelque chose?
Gik
11
C'est une perte de temps pour les développeurs de suivre la voie de chaque cas exceptionnel qui n'est pas censé se produire du tout dans des circonstances normales. Cela implique de réfléchir à des moyens appropriés pour informer l'utilisateur de chacun d'eux et / ou de rendre l'application suffisamment robuste pour continuer de manière attendue après leur apparition. L'approche la plus pratique consiste à planter l'application et à corriger le bogue trouvé dans le rapport de plantage et à publier une nouvelle version. Cela dit, il est important de s'assurer qu'il n'y a pas de perte de données dans une telle situation. Cela doit néanmoins être assuré, mais c'est un travail bien moindre.
trss
20

Je ne peux pas vraiment parler à NSAssert, mais j'imagine que cela fonctionne de manière similaire à assert () de C.

assert () est utilisé pour appliquer un contrat sémantique dans votre code. Qu'est-ce que cela signifie, demandez-vous?

Eh bien, c'est comme vous l'avez dit: si vous avez une fonction qui ne devrait jamais recevoir de -1, vous pouvez demander à assert () d'appliquer cela:

void gimme_positive_ints (int i) {
  assert (i> 0);
}

Et maintenant, vous verrez quelque chose comme ça dans le journal des erreurs (ou STDERR):

Échec de l'assertion i> 0: fichier example.c, ligne 2

Ainsi, non seulement il protège contre les entrées potentiellement mauvaises, mais il les enregistre de manière standard et utile.

Oh, et au moins en C, assert () était une macro, vous pouvez donc redéfinir assert () comme un no-op dans votre code de version. Je ne sais pas si c'est le cas avec NSAssert (ou même assert () plus), mais c'était assez utile de compiler ces vérifications.

Mando Escamilla
la source
2
Oui, NSAssert est également une macro.
Martin Wickman
18

NSAssertvous donne plus que simplement planter l'application. Il vous indique la classe, la méthode et la ligne où l'assertion s'est produite. Toutes les assertions peuvent également être facilement désactivées à l'aide de NS_BLOCK_ASSERTIONS. Le rendant ainsi plus adapté au débogage. D'un autre côté, lancer un NSExceptionne fait que planter l'application. Il n'indique pas non plus l'emplacement de l'exception et ne peut pas non plus être désactivé si simplement. Voyez la différence dans les images ci-dessous.

L'application se bloque car une assertion déclenche également une exception, comme l' indique la documentation NSAssert :

Lorsqu'il est appelé, un gestionnaire d'assertions imprime un message d'erreur qui inclut la méthode et les noms de classe (ou le nom de la fonction). Il déclenche ensuite une exception NSInternalInconsistencyException.

NSAssert:

Journaux après une assertion

NSException:

Journaux après une exception

Abdurrahman Mubeen Ali
la source
An NSExceptionoffre de nombreuses possibilités de personnaliser la sortie qu'il renvoie via les paramètres reasonet userInfo. Il n'y a aucune raison pour laquelle vous ne pouvez pas ajouter le nom de classe, le sélecteur, les informations de ligne et tout ce que vous souhaitez ajouter pour aider au débogage. À mon humble avis, vous utilisez un NSAssertà des fins de débogage pendant le développement, mais vous les désactivez pour l'expédition; vous lancez un NSExceptionsi vous souhaitez laisser une assertion dans le code d'expédition.
markeissler
17

En dehors de ce que tout le monde a dit ci-dessus, le comportement par défaut de NSAssert()(contrairement à C assert()) est de lancer une exception, que vous pouvez attraper et gérer. Par exemple, Xcode fait cela.

Jens Ayton
la source
Y a-t-il plus sur la façon dont nous pouvons attraper et gérer l'exception?
Gon
1
Les exceptions dans le cacao ne sont PAS «capturables et manipulables» de facto. Si le contrôle passe par une fonction Apple n'importe où dans l'arborescence des appels, le comportement n'est pas défini. Les exceptions concernent uniquement les rapports d'erreurs (aka, crittercism, etc.), pas pour un usage général comme en Java.
Michael
9

Juste pour clarifier, comme quelqu'un l'a mentionné mais pas complètement expliqué, la raison d'avoir et d'utiliser des assertions au lieu de simplement créer du code personnalisé (faire des ifs et lever une exception pour de mauvaises données, par exemple) est que les assertions DEVRAIENT être désactivées pour les applications de production.

Lors du développement et du débogage, les assertions sont activées pour vous permettre de détecter les erreurs. Le programme s'arrête lorsqu'une assertion est évaluée comme fausse. Mais, lors de la compilation pour la production, le compilateur omet le code d'assertion et RENDENT VOTRE PROGRAMME PLUS RAPIDE. D'ici là, j'espère que vous avez corrigé tous les bogues. Dans le cas où votre programme a encore des bogues en production (lorsque les assertions sont désactivées et que le programme «saute» les assertions), votre programme finira probablement par planter à un autre moment.

De l'aide de NSAssert: "Les assertions sont désactivées si la macro de préprocesseur NS_BLOCK_ASSERTIONS est définie." Donc, mettez simplement la macro dans votre cible de distribution [uniquement].

Ohad Kravchick
la source
6

NSAssert(et son équivalent stdlib assert) sont de détecter les erreurs de programmation lors du développement. Vous ne devriez jamais avoir une assertion qui échoue dans une application de production (publiée). Vous pouvez donc affirmer que vous ne passez jamais un nombre négatif à une méthode qui nécessite un argument positif. Si l'assertion échoue pendant le test, vous avez un bogue. Si, cependant, la valeur transmise est entrée par l'utilisateur, vous devez valider correctement l'entrée plutôt que de vous fier à l'assertion en production (vous pouvez définir un #define pour les versions de version qui désactive NSAssert*.

Barry Wark
la source
2
+1 parce que votre réponse me paraît la plus logique! L'utilisation de NSAssert est plus logique si elle est destinée au développement, pas après la publication. Un utilisateur qui saisit une valeur qui n'est pas autorisée doit être suivi d'une erreur d'interface utilisateur, et non de NSAssert qui plante l'application. Le brouillard s'est dissipé!
pnizzle
3

Les assertions sont couramment utilisées pour imposer l'utilisation prévue d'une méthode ou d'un élément de logique particulier. Supposons que vous écriviez une méthode qui calcule la somme de deux entiers supérieurs à zéro. Afin de vous assurer que la méthode a toujours été utilisée comme prévu, vous mettriez probablement une affirmation qui teste cette condition.

Réponse courte: Ils imposent que votre code soit utilisé uniquement comme prévu.

Salons
la source
2

Pour répondre pleinement à sa question, le but de tout type d'affirmation est d'aider au débogage. Il est plus intéressant de détecter les erreurs à leur source, que de les détecter dans le débogueur lorsqu'elles provoquent des plantages.

Par exemple, vous pouvez transmettre une valeur à une fonction qui attend des valeurs dans une certaine plage. La fonction peut stocker la valeur pour une utilisation ultérieure et, lors d'une utilisation ultérieure, l'application se bloque. La pile d'appels vue dans ce scénario n'afficherait pas la source de la valeur incorrecte. Il est préférable d'attraper la mauvaise valeur au fur et à mesure qu'elle entre pour savoir qui passe la mauvaise valeur et pourquoi.

Robert Hawkey
la source
-3

NSAssertfaire planter l'application lorsqu'elle correspond à la condition. S'il ne correspond pas à la condition, les instructions suivantes seront exécutées. Recherchez l'EX ci-dessous:

Je viens de créer une application pour tester quelle est la tâche de NSAssert:

    - (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    [self testingFunction:2];
}

-(void)testingFunction: (int)anNum{
    // if anNum < 2 -> the app will crash
    // and the NSLog statement will not execute
    // that mean you cannot see the string: "This statement will execute when anNum < 2"
    // into the log console window of Xcode
    NSAssert(anNum >= 2, @"number you enter less than 2");
    // If anNum >= 2 -> the app will not crash and the below 
    // statement will execute
    NSLog(@"This statement will execute when anNum < 2");
}

dans mon code, l'application ne plantera pas.Et le cas de test est:

  • anNum > = 2 -> L'application ne plantera pas et vous pouvez voir la chaîne du journal: "Cette instruction s'exécutera quand unNum <2" dans la fenêtre de la console du journal outPut
  • anNum <2 -> L'application plantera et vous ne pouvez pas voir la chaîne de journal: "Cette instruction s'exécutera quand anNum <2"

la source
1
Vous l'avez dans l'autre sens. "NSAssert fait planter l'application lorsqu'elle correspond à la condition. Si elle ne correspond pas à la condition, les instructions suivantes s'exécuteront". NSAssert plante l'application si elle ne correspond PAS à la condition et s'exécute normalement si elle ne correspond pas à la condition.
Joe Tam
L'application plante et enregistre le message lorsqu'elle ne remplit pas la condition, sinon elle s'exécute davantage.
Pravin S.