2009-09-03 3 views

Répondre

26

L'utilisation répandue de NSAssert() ne transformera pas ObjC en Eiffel, mais c'est toujours une bonne pratique tant que vous gardez à l'esprit comment il est réellement implémenté et ce qu'il fait. Points à garder à l'esprit à propos de NSAssert():

Xcode ne désactive pas NSAssert() en mode de libération par défaut. Vous devez vous rappeler d'ajouter NS_BLOCK_ASSERTIONS à GCC_PREPROCESSOR_DEFINITIONS dans vous xcconfig. (Vous êtes using xcconfigs, n'est-ce pas?) Un problème commun est d'affirmer non-nul dans les cas où nul ne fonctionnera tranquillement; cela peut signifier des accidents sur le terrain pour des choses qui auraient pu être récupérées gracieusement. Ceci n'est pas lié à la macro NDEBUG utilisée par assert(), et vous devez vous rappeler de définir les deux si votre code inclut les deux types d'assertions.

Si vous compilez NSAssert() en mode édition, vous n'obtiendrez aucune indication d'un problème survenu lorsque les clients vous envoient leurs journaux. J'emballe personnellement NSAssert() dans mes propres macros qui se connectent toujours, même en mode Release.

NSAssert() force souvent la logique dupliquée. Considérons le cas de tester un pointeur C++ pour NULL. Vous utilisez NSAssert(), mais vous encore besoin d'utiliser un test simple if() ainsi éviter de s'écraser sur le terrain. Ce type de code dupliqué peut devenir la source de bogues, et j'ai vu un code qui échoue en raison d'assertions qui ne sont plus valides. (Heureusement, cela est généralement en mode Debug!) J'ai beaucoup discuté comment créer une macro qui combinerait l'assertion et if(), mais il est difficile de le faire sans être fragile ou de rendre le code difficile à comprendre.

En raison du dernier problème, je place généralement "NSAssert(NO, ...)" dans une clause else{} plutôt que d'effectuer une assertion en haut de la méthode. Ce n'est pas idéal parce que cela éloigne le contrat de la signature de la méthode (réduisant ainsi son avantage documentaire), mais c'est l'approche la plus efficace que j'ai trouvée dans la pratique.

+0

Je ne vois pas pourquoi vous finiriez avec le code en double. Si l'assertion échoue, votre code n'a aucune obligation de se sauver d'un crash. En fait, cela ne sert à rien, car une supposition que vous avez faite a été brisée, donc il n'y a pas de manière saine de sauver le programme de ne pas s'écraser plus tard, en raison d'un état incohérent. –

+0

Votre code a toujours l'obligation de bien se comporter pour l'utilisateur. Alors que dans certains cas, la corruption de données est préoccupante, l'écrasement immédiat est la seule approche. Mais dans de nombreux cas, ce n'est pas vrai et la récupération, montrant une erreur ou même ne rien faire est préférable dans Release vs plantage et laisser l'utilisateur regarder Springboard sans aucune information. J'ai vu assez d'assertions incorrectes dans le code de production pour se méfier du crash préemptif dans Release en l'absence de corruption possible des données. Debug est une situation complètement différente, et je préconise de s'écraser rapidement. –

+0

Une bonne application reconnaît qu'il a des bugs et cherche à les contenir. Juste parce que j'ai un bug mineur dans le système d'aide ne devrait pas signifier que le jeu entier devrait planter juste parce que techniquement le "programme est maintenant indéfini." Chaque vrai programme de n'importe quelle taille implique beaucoup de comportement indéfini. C'est notre responsabilité de minimiser cela, mais aussi d'y vivre et de ne pas planter à chaque fois que quelque chose semble étrange. –

7

Mise au point. Chaque fois que vous écrivez du code, vous faites presque toujours des suppositions. Hypothèses sur l'état de l'environnement, les valeurs de vos paramètres, vos variables et champs locaux, etc. Souvent, ces hypothèses sont fausses (un ancien collègue m'a donné une bonne maxime, que "l'Assomption est la mère de tous les fsckups") .

Des affirmations existent pour valider vos hypothèses, au moment où vous les faites. Vous avez une méthode

void foo(int x) 
{ 
    … 
} 

que vous connaissez et avez documenté ne fonctionne que pour x> 5? Affirmez-le!

Les assertions côtoient les tests unitaires et les méthodes formelles dans le cadre d'une bonne pratique de codage. Alors que certains pourraient penser que des tests unitaires complets garantissent que les assertions sont redondantes, ce n'est pas le cas. Par exemple, vous pouvez avoir une hypothèse à une méthode qui, disons, les employés ont toujours plus de 16 ans et moins de 100 ans - mais que le code, pour le moment, ne l'exige pas. Les tests unitaires qui réussissent ces paramètres réussiront, mais plus tard, lorsque vous aurez besoin d'utiliser votre hypothèse, vous aurez du code partout où les tests auront réussi, mais c'est faux.

9

Les principaux avertissements sont des effets secondaires.Si vous écrivez:

NSAssert([self.navigationController popViewControllerAnimated:YES]!=nil,@"Something fails"); 

popViewControllerAnimated: seront exécutés dans la version de débogage, mais pas dans la version que les bandes NSAssert(). Cela signifie que votre version de version se comportera différemment de la version de débogage.

Ce problème disparaît si vous êtes assez prudent:

UIViewController* vc = [self.navigationController popViewControllerAnimated:YES]; 
NSAssert(vc!=nil,@"Something fails"); 
+0

+1 perspicace, bien que je n'appelle jamais des méthodes dans les affirmations. –

Questions connexes