Benvenuti alla parte sei della serie Mobilityuts + Beginning iOS Development. Questa rata coprirà i fondamenti di debug di Xcode. Comprenderà una breve quantità di teoria del debug del software e un'applicazione pratica per dimostrare l'uso dei breakpoint e del debugger Xcode. L'articolo si concluderà con alcuni suggerimenti generali e le migliori pratiche, nonché un elenco di risorse utili disponibili per continuare il tuo apprendimento.
Problemi con la visualizzazione dei supporti sopra? Accedi alla versione completa e di alta qualità di questo video nel suo formato originale, MOV.
Invece di creare una nuova applicazione appositamente per questo tutorial, ho preso l'applicazione FortuneCrunch che abbiamo creato nella seconda parte di questa serie e ho introdotto una serie di errori diversi nel codice sorgente. Scarica BrokenCrunch, la versione spezzata di FortuneCrunch che accompagna questo post, seguire come dimostrerò come utilizzare gli strumenti di debug di Xcode.
Prima di iniziare effettivamente il debug di BrokenCrunch, parliamo per un momento della teoria del debug del software. In generale, gli errori software (noti anche come "bug") possono essere classificati come segue:
Come suggerisce il nome, si verifica un errore in fase di compilazione quando si tenta di compilare il codice sorgente dell'applicazione. In Xcode, questo accade quando selezioni "Build and Run" o "Build and Debug" per avviare l'applicazione sul dispositivo o sul simulatore. Quando viene rilevato un errore in fase di compilazione, impedisce letteralmente all'avvio dell'applicazione. Come vedremo, gli errori in questa categoria possono derivare da dichiarazioni di sintassi non sensate o errate o da problemi che si presentano nella fase di collegamento della build dell'applicazione. In generale, gli errori in fase di compilazione sono le più facili delle tre categorie da risolvere in quanto il compilatore in genere emette un messaggio di errore o un messaggio di avviso significativo che ti avviserà della natura del problema.
Gli errori di runtime si verificano dopo che l'applicazione è stata compilata e avviata nel simulatore o su un dispositivo. Un arresto anomalo dell'applicazione o una perdita di memoria che si verifica a causa della scarsa gestione della memoria oggetto è un esempio di errore di runtime.
Un errore logico si verifica durante la fase di esecuzione di un'applicazione e genera un comportamento inatteso o indesiderato delle applicazioni che è in conflitto con il risultato previsto dello sviluppatore del software o dello stakeholder del progetto. Un buon esempio di errore logico è una formula matematica che è stata implementata in modo errato. Considera il teorema di Pitagora:
Se uno sviluppatore di software ha implementato involontariamente questa formula come:
Il risultato sarebbe un errore logico, ma molto probabilmente non causerebbe l'arresto anomalo dell'applicazione. Questo è ciò che rende gli errori logici così pericolosi: l'applicazione può apparentemente funzionare "senza bug" per lo sviluppatore mentre in realtà produce output non validi o indesiderati.
Con questa conoscenza teorica, apri BrokenCrunch e cominciamo. Dopo aver caricato la nostra applicazione di esempio, selezionare Build> Build and Debug. Noterai che l'applicazione non si avvia e il compilatore ha generato un numero di errori. Per visualizzare i risultati della compilazione tentata, selezionare Build> Build Results.
La selezione degli errori elencati ti porterà direttamente alla riga di codice in cui viene segnalato l'errore. Una cosa importante da tenere presente, tuttavia, è che il numero di errori segnalati dal compilatore e i numeri di riga di tali errori dovrebbero essere considerati la "migliore ipotesi" di ciò che è sbagliato nella propria applicazione, non una dichiarazione conclusiva.
Infatti, un semplice errore di sintassi può causare la segnalazione di errori multipli da parte del compilatore apparentemente non correlati al problema. Ad esempio, dai un'occhiata al "Parentesi prevista prima di" setImage "" linea di errore. Se esamini la linea in questione, dovresti scoprire che la sintassi è perfetta. A quanto pare, il problema qui non è sulla linea riportata, ma sulla linea appena sopra. Capisci il problema?
Il NSLog ()
la dichiarazione non è stata terminata con un punto e virgola. Ciò significa che il compilatore non sa che intendevi terminare la riga dopo l'ultima parentesi, e sta visualizzando tutto da NSLog
alla parentesi chiusa finale e punto e virgola dopo UIControlStateNormal
come una dichiarazione.
Aggiungi il punto e virgola per completare il NSLog
dichiarazione:
NSLog (@ "In crunchCookie");
Salvare il file sorgente e fare nuovamente clic su "Build and Debug". Due dei tre errori visualizzati in origine dovrebbero ora essere risolti.
Quindi, selezionare il "Nessuna dichiarazione di proprietà" errore. Come puoi vedere, questo errore sta segnalando che la proprietà che stiamo tentando di sintetizzare non esiste. Per verificare ciò, aprire il file FortuneCrunchViewController.h in cui la proprietà avrebbe dovuto essere dichiarata. Se esamini la riga 17, la sintassi è corretta, ma abbiamo una discrepanza tra la proprietà che abbiamo dichiarato e quella che stiamo tentando di sintetizzare. Objective-C è un linguaggio sensibile al maiuscolo / minuscolo, nel senso che la "C" nel cookie deve essere in maiuscolo per corrispondere alla proprietà che stiamo tentando di sintetizzare. Aggiorna la dichiarazione di proprietà nel file di intestazione per leggere:
@property (nonatomic, retain) IBOutlet * fortuneCookieButton;
Salva il file sorgente e crea e fai il debug di nuovo. Questa volta, piuttosto che aprire i risultati di costruzione da Build> Build and Debug, fai semplicemente clic sull'icona dell'errore nell'angolo in basso a destra di Xcode.
Un passo in avanti, quattro passi indietro. L'errore relativo alla linea di proprietà synthesize è scomparso, ma abbiamo una lista completamente nuova di errori. Quello che è successo?
Questo è un buon momento per prendere nota delle diverse fasi mostrate nella finestra Risultati build:
Si noti che abbiamo un avvertimento sotto la sezione "Compila" dell'output dei risultati di compilazione. Questa è la stessa sezione in cui sono stati segnalati i nostri errori precedenti. Ora che i precedenti tre errori sono stati risolti, siamo stati in grado di passare dalla fase di compilazione alla fase di collegamento della nostra build dell'applicazione, e tutti i nuovi errori sono errori di collegamento. Quando si verifica un errore di collegamento, è in genere perché si sta tentando di utilizzare le funzioni da un framework che non è stato effettivamente incluso nell'applicazione. In questo caso, i risultati di costruzione fanno riferimento a una funzione chiamata _UIApplicationMain
nel file main.o. Main.o è la versione compilata, in codice macchina di main.m. Diamo un'occhiata al codice sorgente in quel file. Alla riga 13 è possibile visualizzare una chiamata a funzione UIApplicationMain
:
int retVal = UIApplicationMain (argc, argv, nil, nil);
UIApplicationMain
è una funzione centrale per ogni applicazione iOS, ma come si può scoprire di più su di esso e capire quale quadro è incluso all'interno? Fortunatamente, l'SDK di iOS ha una buona documentazione. Se si tiene premuto il tasto opzione (o alt) e si fa doppio clic sul nome della funzione, si avvierà un estratto dalla documentazione ufficiale dell'SDK iOS che discute questa funzione. Fai clic sull'icona "libro" in alto a destra per visualizzare la documentazione completa disponibile. Si può vedere che così facendo è stata avviata la documentazione di riferimento della funzione per il framework UIKit. Bingo, abbiamo il nostro quadro mancante. Tuttavia, prima di aggiungere il framework al progetto, esaminiamo un altro metodo che potresti aver usato per determinare l'origine di UIApplicationMain
.
Chiudi la finestra della documentazione. Ora, tieni premuto il pulsante di comando e fai doppio clic su UIApplicationMain
funzione. Si sta ora esaminando l'origine di UIApplication.h, il file di dichiarazione dell'intestazione che contiene il file UIApplicationMain
dichiarazione di funzione. Se scorri verso l'alto della finestra, vedrai che questo file importa più altre intestazioni UIKit e che il commento nella parte superiore include il nome del framework "UIKit".
Passiamo alla risoluzione di questi errori di collegamento includendo il framework UIKit. Per fare ciò, fare clic con il tasto destro del mouse o fare clic con il pulsante destro del mouse sulla cartella Frameworks nel riquadro Groups & Files e selezionare aggiungi> quadri esistenti. Trova il framework UIKit e fai clic su "Aggiungi". Per testare il nostro lavoro, seleziona nuovamente Compila ed esegui il debug.
Come puoi vedere, il simulatore è stato lanciato con successo e siamo in grado di visualizzare la nostra applicazione. Ciò significa che abbiamo risolto tutti gli errori in fase di compilazione nella nostra applicazione.
Vai avanti e fai clic sul biscotto della fortuna ... come puoi vedere, facendo clic sul cookie si ottiene un errore in fase di esecuzione e l'applicazione si è arrestata in modo anomalo. Il messaggio visualizzato nella parte inferiore sinistra della schermata Xcode non è molto utile, quindi diamo un'occhiata più da vicino aprendo la console.
La console visualizza sia una serie di chiamate di ciò che stava accadendo nell'esecuzione del nostro programma al momento del crash, sia una spiegazione più dettagliata: "Termina l'applicazione a causa dell'eccezione non rilevata ... FortuneCrunchViewController cookieCruncher: selettore non riconosciuto inviato all'istanza." Questo messaggio indica che il nostro pulsante sta chiamando il selettore errato per l'evento che abbiamo attivato facendo clic sul cookie. Poiché l'interfaccia per FortuneCrunch è stata creata in Interface Builder, apriamo il file XIB di Interface Builder per "FortuneCrunchViewController" per dare un'occhiata più da vicino.
Seleziona il pulsante cookie e controlla clic o clic destro per visualizzare un elenco di azioni connesse:
Puoi vedere che l'evento Ritocco dentro fa riferimento a un obiettivo che non esiste, indicato dal testo giallo. Rimuovi l'inesistente target "cookieCruncher" e ricollega touchUpInside al proprietario del file selezionando il target "crunchCookie" visualizzato nel menu a discesa. Salva il tuo lavoro in Interface Builder, torna a Xcode e riavvia l'applicazione.
Facendo nuovamente clic sul cookie della fortuna si genera un errore in fase di esecuzione. Questa volta, il messaggio della console non è così utile, viene solo visualizzato "EXC_BAD_ACCESS".
Dai un altro sguardo ai risultati di costruzione selezionando Build> Build Results. Hai notato l'avviso prima? Gli avvisi del compilatore sono spesso un'indicazione di un potenziale errore di runtime, ma poiché non c'è nulla di errato con la sintassi effettiva della riga per cui è stato emesso l'avviso, il compilatore è comunque in grado di creare l'applicazione con successo. Certo, ci sono momenti in cui un avvertimento del compilatore è una "falsa bandiera" e non si tradurrà in un errore in fase di esecuzione, ma verso l'alto del 95% delle volte, se il compilatore ha emesso un avviso si sta facendo qualcosa di sbagliato.
Fare clic sull'avviso per saltare alla riga del codice sorgente in cui si è verificato.
L'avviso si riferisce a tipi di puntatori incompatibili. capisci il problema? Il metodo imageNamed prevede un oggetto NSString, ma questa linea di codice fornisce il metodo con una stringa di stile C letterale. Aggiungi il simbolo "@" per rendere questa una stringa Objective-C:
[fortuneCookieButton setImage: [UIImage imageNamed: @ "cookie-closed.png"] forState: UIControlStateNormal];
Salva i tuoi progressi ed esegui nuovamente l'applicazione.
Questa volta, quando fai clic sul biscotto della fortuna, si verifica un errore logico: l'applicazione non si arresta e l'etichetta "Happy iPhone Hacking" viene visualizzata come previsto, ma l'immagine di sfondo rimane come cookie chiuso.
Per risolvere questo problema, diamo un'occhiata alla funzione responsabile della transizione: (IBAction) crunchCookie
. La riga 19 è responsabile della modifica dell'immagine di sfondo e puoi vedere che sta impostando la nuova immagine di sfondo su "cookie-closed.png". Se dai un'occhiata a cookie-closed nella cartella Risorse, vedrai che questa è in effetti la stessa immagine visualizzata quando viene caricata prima l'app. Dobbiamo cambiare quella linea per passare a "cookie-crunched.png":
[fortuneCookieButton setImage: [UIImage imageNamed: @ "cookie-crunched.png"] forState: UIControlStateNormal];
Crea ed esegui nuovamente l'applicazione ... e ora toccando il cookie si ottiene l'immagine di sfondo prevista con l'etichetta visualizzata correttamente.
Congratulazioni! Hai appena attraversato il processo di correzione degli errori in fase di compilazione, degli errori di runtime e degli errori logici in un'applicazione. Per tutto il tempo, abbiamo a malapena sfruttato i potenti strumenti di debug disponibili con Xcode.
Per continuare la nostra esplorazione degli strumenti di debug più avanzati disponibili, proviamo ad estendere l'app FortuneCrunch per renderla un po 'più interessante. Piuttosto che visualizzare la stringa statica "Happy iPhone Hacking!" Ogni volta che il cookie viene scricchiolato, costruiamo una serie di più NSString
valori che potrebbero essere visualizzati.
Tornare a Xcode e aprire il file FortuneCrunchViewController.h. Aggiungi il seguente membro dati:
NSArray * fortune;
Questo array verrà utilizzato per contenere le nostre stringhe di fortuna casuali.
Ora, aggiungi la seguente firma del metodo:
-(NSString *) generateRandomFortune;
Questa linea dichiarerà un nuovo metodo nella nostra classe che sarà usato per selezionare una fortuna casuale dal nostro array di fortune.
Quindi, passare a FortuneCrunchViewController.m. Dal momento che questa classe verrà avviata dal nostro file XIB, abbiamo bisogno di sovrascrivere il initWithCoder
metodo e allocare la matrice che abbiamo dichiarato nel file .h, inizializzandola con alcune nuove fortune:
-(id) initWithCoder: aDecoder self = [super initWithCoder: aDecoder]; if (self) fortunes = [[NSArray alloc] initWithObjects: @ "Colui che getta lo sporco perde terreno.", @ "Una bocca chiusa non raccoglie piedi.", @ "Aiuto! Sono un prigioniero in una panetteria! ", Nil]; return self;
Ora che abbiamo creato un nuovo NSArray
, non dimenticare di rilasciarlo nel dealloc
metodo:
-(void) dealloc [rilascio di fortune];
Passiamo alla codifica del generateRandomFortune
funzione:
-(NSString *) generateRandomFortune int chosen_index = arc4random ()% 3 * 10; return [fortunes objectAtIndex: chosen_index];
Queste linee generano semplicemente un nuovo numero di indice casuale che useremo per restituire la stringa di fortuna corrispondente.
Infine, modifica il crunchCookie
metodo per utilizzare una delle nostre fortune casuali piuttosto che il testo statico "Happy iPhone Hacking!":
fortuneLabel.text = [self generateRandomFortune];
Crea ed esegui l'applicazione dopo aver salvato queste modifiche. Se fai clic sul cookie, creerai un errore in fase di esecuzione. Per capire perché questo sta accadendo, useremo il debugger Xcode ei punti di interruzione personalizzati.
Un breakpoint è un flag che segnala all'applicazione che l'esecuzione del programma dovrebbe "mettere in pausa" quando viene raggiunta la linea con il punto di interruzione. L'esecuzione dell'applicazione in "Modalità di costruzione e debug" consente di utilizzare i punti di interruzione. Per impostare un punto di interruzione, è sufficiente fare clic sull'editor "grondaia" sulla linea che si desidera attivare un punto di interruzione. Per capire cosa sta succedendo nella nostra applicazione, stabiliremo il nostro punto di interruzione su NSLog
linea, subito dopo viene chiamato il metodo crunchCookie:
Costruisci ed esegui il debug dell'applicazione con questo nuovo breakpoint.
Dopo il caricamento dell'applicazione, fai clic sul cookie. Se guardi in basso a sinistra su Xcode, vedrai il messaggio di stato "Fermati al punto di interruzione 1". Ciò significa che il debugger ha interrotto con successo l'esecuzione del programma nel punto di interruzione impostato. Noterai inoltre che una freccia rossa indica la linea di esecuzione corrente in cui il debugger ha "messo in pausa" il programma.
Quindi, cosa puoi fare con il debugger? Più che può essere coperto in un singolo tutorial. Tuttavia, ci sono tre azioni fondamentali che puoi intraprendere a questo punto: vai oltre, entra ed esci. Tutte queste opzioni sono disponibili dalla barra dei menu del debugger nel codice.
Se si preme il pulsante "passaggio" sul menu di debug in-code, si noterà che l'esecuzione del programma continua alla riga successiva. "Passaggio sopra" continuerà semplicemente l'esecuzione di una riga alla volta all'interno del metodo corrente, ma non seguirà l'esecuzione del codice se si avvia a un altro metodo. Se si desidera effettivamente seguire l'esecuzione del codice in altre chiamate di metodo nel codice, sarà necessario utilizzare il pulsante "step in".
Come puoi vedere, entrare ci ha effettivamente portato nel generateRandomFortune
metodo, che è esattamente ciò che vogliamo. Fai di nuovo clic su "Passa sopra" per vedere cosa succede quando arc4random ()
è chiamato. Non sarebbe bello se sapessimo a cosa è stata appena impostata la variabile selected_index? Fortunatamente, possiamo! Una delle migliori caratteristiche dell'utilizzo del debugger è la possibilità di scorrere le variabili semplicemente per vedere rapidamente il loro valore.
Chiaramente, il chosen_index
il valore è molto più grande della lunghezza del nostro array. A differenza di altri linguaggi di programmazione, la funzione di randomizzazione che utilizziamo restituirà un numero intero, quindi non è necessario convertire da un numero decimale a un numero intero moltiplicando il valore per 10. Aggiornare la riga da leggere:
int chosen_index = arc4random ()% 3;
Abbiamo finito di apportare modifiche a questa funzione, quindi utilizza il pulsante "Esci" per uscire da questa sottofunzione e tornare a crunchCookie
. Nota che anche se non l'abbiamo visto, il resto della funzione è stato eseguito normalmente.
Infine, prendi nota del pulsante di interruzione "Attiva / Disattiva" e del pulsante "Continua esecuzione" nella barra dei menu del debugger in codice. "Continue Execution" consentirà semplicemente l'esecuzione del programma per continuare normalmente. Puoi pensare ad esso come il pulsante "pausa". Vai avanti e premi questo ora.
Prima di passare alla disattivazione dei breakpoint, c'è un altro problema da affrontare: ciò che hai appena sperimentato è chiamato "debugger in-code". È molto potente, ma sono disponibili anche altre due modalità di debug: la finestra completa del debugger e la prospettiva del mini-debugger.
Per accedere alla finestra del debugger completo, fare clic sull'icona "debug" sulla barra dei menu del debugger in-code. Questa finestra ha molte più informazioni rispetto al debugger in-code. Sulla sinistra c'è una traccia dello stack che mostra il contesto dell'esecuzione del programma (hai anche la possibilità di selezionare uno qualsiasi dei thread generati attualmente). A destra, è possibile visualizzare una rapida visualizzazione delle varie variabili attualmente memorizzate. La selezione di una firma diversa dello stack di chiamata modificherà la visualizzazione nel Debugger. Puoi cambiare il layout della finestra del Debugger andando a Esegui> Visualizzazione debugger.
Infine, il mini-debugger è l'ennesima prospettiva di debug a tua disposizione. Uso raramente questa prospettiva, ma è a tua disposizione Esegui> Mini-debugger.
Poiché abbiamo appena risolto l'errore introdotto nel nostro codice fortuna casuale, non è più necessario che il debugger sia attivo. Disattiva i breakpoint. Tuttavia, prima di creare nuovamente l'applicazione, regoliamo la dimensione del carattere della nostra etichetta di fortuna.
Apri Interface Builder, seleziona l'etichetta e cambia il carattere in Inspector in Arial Black, 9 punti, quindi seleziona la casella "Adatta al formato" e modifica la dimensione minima del carattere a 6 punti. Ora, costruisci ed esegui di nuovo il nostro progetto.
Ecco! La nostra applicazione ora funziona come volevamo.
Ora che hai introdotto le nozioni di base sull'uso del Debugger in Xcode, considera l'applicazione delle seguenti linee guida nel tuo flusso di lavoro di sviluppo quotidiano:
Sebbene il simulatore sia un metodo utile per testare un'applicazione durante la fase di sviluppo del prodotto, non è un sostituto per il test su un dispositivo fisico. Questo perché il simulatore e un dispositivo iOS differiscono in modi importanti e fondamentali. Ad esempio, il simulatore è ovviamente in esecuzione su OS X e il filesystem su OS X non fa distinzione tra maiuscole e minuscole. Tuttavia, il file system su iOS è case sensitive. Quindi riferendosi al file cookie-CRUNChed.png
, invece di cookie-crunched.png
, funzionerà bene nel simulatore ma fallirà su un dispositivo iOS reale. Un'altra considerazione chiave è che il simulatore ha molta più memoria disponibile di un dispositivo reale, e questo fatto avrà spesso un impatto notevole sull'esperienza utente. Infine, non tutte le applicazioni predefinite fornite con iOS sono disponibili nel simulatore, inclusi i programmi Mappe e App Store. Ciò significa che non sarai in grado di testare il codice che genera indicazioni stradali con l'app Maps o promuove le applicazioni incrociate nell'App Store nel simulatore. Queste sono solo alcune delle differenze esistenti. Consiglio vivamente di testare sul maggior numero possibile di dispositivi fisici iOS che eseguono il maggior numero possibile di versioni mirate di iOS.
Clang Static Analyzer è uno speciale strumento di analisi statica C / Objective-C fornito con Xcode. Questo strumento è in grado di analizzare il codice per errori o incongruenze che altrimenti potrebbero passare inosservati.
Mentre i dettagli di come funziona l'analizzatore vanno oltre lo scopo di questo articolo, usarlo è fortunatamente molto facile. Per eseguire un'analisi statica del tuo codice, seleziona semplicemente Build> Build and Analyze dal menu di build di Xcode.
Se vuoi saperne di più su come funziona l'opzione "Costruisci e Analizza", puoi leggere online Static Code Analysis e Clang Static Analyzer.
In questo tutorial, abbiamo appreso come funzionano i breakpoint impostando i breakpoint specifici del progetto nel nostro codice. Oltre ai punti di interruzione specifici del progetto, Xcode consentirà anche di impostare punti di interruzione "globali" che si applicheranno a tutti i progetti iOS creati in Xcode. Impostazione di un punto di interruzione globale su objc_exception_throw
ti consentirà di avviare automaticamente il debugger ogni volta che si verifica un'eccezione (un tipo di errore di runtime). Per ulteriori informazioni sui vantaggi di questo approccio e su come implementarlo in codice, fare riferimento al mio Suggerimento rapido iOS su objc_exception_throw e punti di interruzione globali.
Come accennato in precedenza in questo tutorial, la maggior parte degli avvisi del compilatore che vengono emessi dovrebbe essere risolta prima di avviare il progetto e non ci dovrebbe mai essere uno scenario quando il codice che genera gli avvisi del compilatore dovere rimangono invariati affinché un'applicazione funzioni correttamente. Di conseguenza, alcuni programmatori consigliano di considerare tutti gli avvisi del compilatore come errori, forzandoli quindi a risolversi come parte del normale processo di sviluppo.
Per tutti tranne alcuni casi marginali, sostengo questa idea e Xcode lo rende facile da implementare. Vai a Progetto> Modifica impostazioni progetto e quindi selezionare la scheda Costruisci. Digita "Tratta avviso" nella barra di ricerca e vedrai un valore booleano chiamato "Considera gli avvisi come errori". Seleziona questa casella per abilitare la funzione.
Un altro passo che puoi compiere per aumentare le probabilità che la tua applicazione venga accettata la prima volta che la presenti su iTunes Store è di abilitare il flag "Crea e convalida" nelle impostazioni di generazione del progetto. Digitare "convalida" nella casella di ricerca della scheda di creazione delle impostazioni del progetto, quindi selezionare "Convalida prodotto build". Questa opzione eseguirà alcuni test eseguiti dai revisori Apple, consentendo di anticipare potenzialmente un rifiuto di App Store. Va notato che il controllo di questa casella non garantisce che la tua app passerà in rassegna l'App Store, ma è meglio di niente.
Oltre alla console, ai risultati di costruzione e al Debugger, ci sono alcuni altri grandi strumenti di debug e ottimizzazione di cui dovresti essere a conoscenza nei tuoi sforzi di sviluppo. Per ulteriori letture, dai un'occhiata alla documentazione per i seguenti strumenti:
Questo è stato un vorticoso tour di debugging con l'SDK di iOS. C'è ancora molto altro da fare, ma si spera che questa lezione sia stata sufficiente per aiutarti a risolvere più rapidamente i bug nelle tue applicazioni e scrivere software migliore! Se desideri saperne di più su alcuni degli strumenti avanzati discussi in questo tutorial, come Strumenti, Shark o SpinControl, o se desideri saperne di più sul debug in generale, lascia un commento qui sotto e fammi sapere!