Science Blog





Original article:

https://www.mikeash.com/pyblog/friday-qa-2013-05-03-proper-use-of-asserts.html

Vineri Q & A 2013-05-03: Utilizarea corectă a afirmă

de Mike Ash

Afirmă sunt un instrument puternic pentru construirea cod de calitate, dar sunt de multe ori prost inteleasa. Astăzi, vreau să discute diferitele opțiuni de scriere le invocă aplicații de cacao și cele mai bune moduri de a ei, un subiect sugerat de cititor Ed Wynne folosesc.

API-uri

Fundamental, o afirma este doar un apel care ia o expresie și indică eșec într-un fel, dacă expresia nu este adevărat.Ideea de bază este de a verifica pentru condiții care trebuie să fie întotdeauna adevărat, astfel încât să nu mai devreme și, evident, mai degrabă decât mai târziu și în lipsa confuzie. De exemplu, o matrice dereference va eșua în diverse moduri ciudate dacă dau un indice de rău:

x = array[index]; // Sigur Indicele speranță este în intervalul

Folosind un afirma poate ajuta la a face evident ceea ce a mers bine:

assert(index >= 0 && index < arrayLength);
x = array[index];

Aceasta demonstrează, de fapt un API pentru afirmă. C oferă funcția assert dacă #include <assert.h>. Este nevoie de un singur expresie. Dacă expresia este adevărată, ea nu face nimic. Dacă e fals, se imprimă expresia, numele fișierului și numărul liniei în care se află assert, și apoi solicită anulare, de încheiere a programului.

Cacao oferă mai multe funcții afirma, de asemenea. Cel mai de bază este NSAssert. Este nevoie de o expresie și o descriere șir, care poate fi un șir format:

NSAssert(x != y, @"x and y were equal, this shouldn't happen");
NSAssert(z > 3, @"z should be greater than 3, but was actually %d", z);
NSAssert(str != nil, @"nil string while processing %@ of type %@", name, type);

Ca funcția assert, aceasta înregistrează eșecul afirmație dacă expresia este falsa. Apoi aruncă o NSInternalInconsistencyException, și ce se întâmplă atunci depinde de stivuitoare excepție sunt prezente. Într-o aplicație tipică de cacao, acesta va fi prins, fie și autentificat de către runloop, sau va încheia aplicarea.

Din păcate, exploatarea forestieră din NSAssert este slab. Acesta înregistrează faptul că afirmația nu a reușit, precum și metoda a fost și numărul numele fișierului și linia, dar nu jurnal de fapt expresia care nu a reușit, nici nu jurnal șir motiv furnizate macro. Excepția se aruncă nu include șir motiv, cel puțin, atâta timp cât excepție se tipărită la un moment dat, care va apărea.

Există câteva variante ale acestui apel disponibile în Cocoa. Apelul NSAssert funcționează numai într-o metodă de Objective-C, deci nu e un NSCAssert apel echivalent, care funcționează într-o funcție C. Există, de asemenea NSParameterAssert, care nu ia un șir descriere și este destinat pentru verificarea rapid o valoare parametru, și un NSCParameterAssert echivalent pentru funcțiile C.

Construi propriul dvs.

Opțiunile built-in nu sunt mari. C assert este decent, dar nu permite un mesaj personalizat. Apelurile de cacao au logare rău, și comportamentul lor în caz de afirmare nu depinde prea mult de context rulare, și nu poate înceta de fapt aplicația.

Aceste lucruri nu sunt greu pentru a construi, deși, așa că haideți să construim una care face lucrurile bine! Vom dori un apel care ia o expresie și, opțional, un șir format descriere:

MAAssert(x > 0);
MAAssert(y > 3, @"Bad value for y");
MAAssert(z > 12, @"Bad value for z: %d", z);

Acesta ar trebui să jurnal expresia, șirul format în cazul în care există, și numele fișierului, numărul liniei, și denumirea funcției în care a apărut problema. În plus, aceasta ar trebui să evalueze numai parametrii șir format dacă afirmația nu, pentru a face lucrurile mai eficient. Toate acestea necesită o macro.

Ca toate macrocomenzile bun multi-line, acest macro este înfășurat într-o do / timp construct:

#define MAAssert(expression, ...) \
        do { \

Primul lucru pe care o face este de a verifica dacă expresia este de fapt falsă:

if(!(expression)) { \

Dacă este, se folosește NSLog să vă conectați detaliile eșecului:

NSLog(@"Assertion failure: %s in %s on line %s:%d. %@", #expression, __func__, __FILE__, __LINE__, [NSString stringWithFormat: @"" __VA_ARGS__]); \

#expression Construct produce un șir literal conține textul expresiei. De exemplu, se va produce “x> 0″ pentru primul apel assert mai sus. Identificatorul __func__ produce numele funcția curentă. __FILE__ �?i __LINE__ ar trebui să fie auto-explicative. Manechinul @ “”, în stringWithFormat: apelul se asigură că sintaxa este legal chiar și atunci când este prevăzută nici un șir motiv.

După logare eșecul afirmație, se termină atunci aplicația de apel anulare, iar capetele macro:

    abort(); \
            } \
        } while(0)

Acest lucru funcționează perfect. Acesta permite un șir de motive suplimentare, dar nu o impune pentru cazurile în care expresia este suficient pentru a face clar ce se întâmplă greșit. Întotdeauna solicită abandona pe eșec, mai degrabă decât a aruncat o excepție care ar putea fi prins. Acesta înregistrează toate detaliile disponibile la punctul de eșec.

Cerere de informații specifice

Ar fi minunat dacă am putea obține un mesaj eșec să apară în jurnalele accident, de asemenea. Se pare că, putem! Wil Shipley a demonstrat cum să pună datele personalizate în secțiunea “Informații Application specific” de un jurnal accident. Pune asta undeva în codul sursă:

const char *__crashreporter_info__ = NULL;
asm(".desc ___crashreporter_info__, 0x10");

Orice șir scris în această variabilă globală magie va apărea în acea secțiune a jurnalului accident. Acest lucru nu funcționează peste tot (cuvânt este că nu funcționează pe iOS), dar poate fi la îndemână, și nu face rău atunci când nu funcționează. Dacă doriți să profite de acest lucru, o mică modificare a macro afirma va pune mesajul în această variabilă, precum și de logare se:

#define MAAssert(expression, ...) \
        do { \
            if(!(expression)) { \
                NSString *__MAAssert_temp_string = [NSString stringWithFormat: @"Assertion failure: %s in %s on line %s:%d. %@", #expression, __func__, __FILE__, __LINE__, [NSString stringWithFormat: @"" __VA_ARGS__]]; \
                NSLog(@"%@", __MAAssert_temp_string); \
                __crashreporter_info__ = [__MAAssert_temp_string UTF8String]; \
                abort(); \
            } \
        } while(0)

�?i, ca prin magie, mesajul apare în jurnalul de accident.

Filozofie

Acum, că știi cum să scrie un afirma în mai multe moduri diferite, exact ce fel de afirmă ar trebui să scrie?

Afirmă trebuie scrise pentru conditii care, în conformitate cu înțelegerea dvs. a programului, nu ar trebui să apară. Afirmă nu ar trebui să fie folosite pentru a verifica pentru erori care sunt de fapt de așteptat să se întâmple, în unele cazuri. De exemplu, afirmând că un nume de fișier nu este zero este tehnica buna:

assert(filename != nil);

Cu toate acestea, afirmând că datele pot fi citite de la acel fișier este o practică proastă:

    NSData *data = [NSData dataWithContentsOfFile: filename];
    assert(data != nil);

Care apel poate eșua în mod legitim ca urmare a condițiilor reale, cum ar fi dosarul nu existent pe disc, sau nu au permisiunea de a-l citi. Din acest motiv, acest cod are nevoie de manipulare eroare reale, nu doar o afirma. Neputând să citiți fișierul trebuie să ducă la o abordare alternativă sau de alertare a utilizatorului că ceva a mers prost, nu doar de logare și de încheiere a app.

De obicei, locul cel mai util pentru afirmă este în partea de sus a unei funcții sau a unei metode, pentru a verifica constrângeri asupra parametrilor care nu pot fi exprimate în limba direct. Acestea afirmă corespund direct constrângerilor exprimate în documentația. De exemplu:

 // Flanșă o serie de pinioane. Matrice pinioane trebuie să conțină
    // cel puțin două intrări, și indicele trebuie să fie în matrice.
    - (void)flangeSprockets: (NSArray *)array fromIndex: (NSUInteger)index
    {
        assert([array count] >= 2);
        assert(index < [array count]);

        ...method body...

Diferența dintre un apelant și un apelat face ușor să-și piardă urmări aceste constrângeri, ceea ce face acest un loc excelent pentru a dubla-a verifica dacă totul este așa cum ar trebui să fie. Trebuie acordată o atenție specială parametrilor care sunt ușor de bară și la parametrii unde valorile rele va cauza erori ciudate. De exemplu, aceasta afirma verificarea NULL, în timp ce încă util, nu adăugați prea mult, deoarece accidentul rezultat fără a fi încă destul de clar:

assert(ptr != NULL);
x = *ptr;

Nu e rău, dar timpul poate fi mai bine cheltuite în altă parte. Acest afirma verificarea zero este foarte la îndemână, ca o valoare zero de aici va rezulta doar într-un șir ciudat construit, ceea ce ar putea apărea departe și mult mai târziu:

assert(name != nil);
str = [NSString stringWithFormat@"Hello, %@!", name];

Acesta poate fi, de asemenea, la îndemână pentru a adăuga afirmă în mijlocul cod complex, care are post-condiții pre clare sau. De exemplu, în mijlocul de a modifica o structură de date, s-ar putea asigurați-vă că toate variabilele au valori coerente între ele:

assert(done + remaining == total);

Acest lucru va permite să prinde erori logice repede.

Evitați afirmă pentru condiții evidente care au puțin loc pentru eroare. De exemplu, acestea sunt inutile:

    int x = 1;
    assert(x == 1);

    for(int i = 0; i < 10; i++)
    {
        assert(i >= 0);
        ...

Nu e nici un fel acestea afirmă va declanșa, cu excepția cazului în care computerul este grav defect, astfel încât acestea sunt de fapt o pierdere de timp. Concentreze pe lucruri care “nu se poate întâmpla” dacă părți ale muncii program impreuna așa cum ar trebui, dar care ar putea pierde teoretic.

În cele din urmă, asigurați-vă că condițiile esti afirmarea sunt destul de rapid pentru a evalua. Nu vrei să le bogging jos programul. Nu bucla prin intermediul dvs. matrice milioane elemente afirmând o conditie complex pe fiecare intrare doar din paranoia.

Pe scurt, afirma premise esențiale ale codul, cu un ochi spre lucrurile care te va face durerea dacă nu prins mai devreme. Scopul este de a obtine un picior pe depanare atunci când lucrurile încep să meargă prost.

Dezactivarea afirmă

Dacă veți căuta pe web pentru informații despre afirmă, vei întoarce invariabil discuțiilor despre cum să dezactivați afirmă în comunicat ta construiește. Cele mai multe afirma sisteme au un mod de a dezactiva afirmă program la nivel. Pentru apelul C afirma, stabilirea macro NDEBUG o dezactivează. Pentru Cacao afirma apeluri, setarea macro NS_BLOCK_ASSERTIONS le dezactivează. Există, în general, din două motive invocate pentru dezactivarea afirmă în comunicat se bazează:

  1. Afirmă impune un cost de execuție care nu ar trebui să facă orice plata utilizator. În teorie, dacă ați testat bine, ar trebui să nu întâmpinați erori de afirmare în eliberarea ta construiește oricum.
  2. Un eșec afirmație termină imediat aplicația, care nu-mi place de utilizatori. Prin eliminarea afirmă, vă dau programul o șansă de a continua să funcționeze în fața unui bug.

Cu toate acestea, eu sunt ferm de părere că dezactivarea afirmă în comunicatul construiește este o idee groaznică. Costul de execuție ar trebui să fie neglijabil, iar dacă nu e, atunci ar trebui să refaceți ta afirmă pentru a remedia acest lucru. În ceea ce privește evitarea reziliere app, sustine trebuie scris astfel încât o defecțiune înseamnă întotdeauna că ceva a mers teribil de greșit. Este posibil ca aplicația va continua să funcționeze în fața asta. Este mult mai probabil că o să se prăbușească. Este de asemenea posibil ca acesta va ține de funcționare, dar corupt datele utilizatorilor dumneavoastră. Un accident curat este mult preferat. Nici un cod este liber de bug-uri, și crashing devreme și, evident, atunci când un bug este întâlnită este mult mai bună, chiar și într-un build comunicat rulează pe mașină unui utilizator. Generarea un jurnal accident curat vă va ajuta să depanați eșecurile mai repede.

Exemplu MAAssert macro de mai sus nu are nici un fel built-in pentru a dezactiva acest motiv. Dacă utilizați o instalație afirma diferit, am recomandăm insistent să evitați vreodată transformandu-le off.

Concluzie

Afirmă sunt un instrument valoros pentru producerea de cod mai bine și de a face bug-uri mai ușor de găsit și fix. Afirmă ar fi folosit oriunde exista o constrângere pe o valoare care nu este pusă în aplicare de limbajul. Orientarea mea generală este că, dacă documentul o restricție pentru apelanții, ar trebui, de asemenea, o afirme în codul. Dacă găsiți vreodată scris un cod care devine ați gândit mult, și are variabile ale căror valori trebuie să se refere la unul de altul într-un anumit fel, nu impuse de limba, afirmă că în codul.

Asta e pentru azi. Vineri Q & A este condusă de sugestii cititor ca întotdeauna, așa că, dacă există un subiect pe care doriți să includem aici, vă rugăm să trimiteți. Întoarce-te devreme pentru un alt articol interesant.

Ti-a placut acest articol? Vând o carte plină de ei. Este disponibil pentru iBooks și Kindle, plus un download direct în format ePub PDF și. Este, de asemenea disponibil în hârtie pentru modă veche. Click aici pentru mai multe informații.

 

  • Cars World
  • Cliparts for all
  • STAATSOPER HAMBURG
  • JOURDAN DUNN BODY
  • MATTHEW GOODE
  • MARCEL NGUYEN
  • MAC LOGO
  • HUMAN STOOL
  • ORKUT GATAS JEANS