cat swift/debugging-errori-breakpoint-strumenti.md
Debugging: errori, breakpoint e strumenti
Debuggare Swift significa osservare stato, flusso asincrono, errori e memoria con metodo, non aggiungere print a caso.
Debuggare non è indovinare. È costruire una spiegazione del comportamento del programma usando strumenti.
In Swift userai:
- messaggi del compilatore;
- breakpoint;
- debugger LLDB;
printmirati;- test;
- Instruments;
- log;
- diagnostiche di concurrency e memoria.
Prima il compilatore
Swift spesso ti ferma prima del runtime.
Messaggi su:
- optional non gestiti;
- access control;
Sendable;- actor isolation;
- tipi incompatibili;
trymancante;awaitmancante.
Non trattarli come ostacoli. Sono feedback sul modello.
Breakpoint
Un breakpoint ferma il programma in un punto.
Usalo per rispondere a domande:
- questa funzione viene chiamata?
- con quali parametri?
- quante volte?
- quale ramo dello switch viene preso?
- quale valore è
nil?
Non mettere breakpoint casuali. Formula un’ipotesi.
Conditional breakpoint
Se un ciclo gira mille volte, ferma solo il caso interessante:
riga.quantita == 0
Oppure:
coupon.codice == "WELCOME10"
Questo rende il debug più rapido.
LLDB essenziale
Nel debugger puoi usare comandi come:
po variabile
p variabile
bt
po: print object, utile per descrizioni leggibili;p: stampa espressione;bt: backtrace, cioè pila delle chiamate.
Non serve diventare esperti subito, ma questi tre comandi aiutano molto.
Debug async
Con async/await, il flusso può sospendersi. Chiediti:
- dove sono gli
await? - quale task sta eseguendo?
- il task è stato cancellato?
- il catch sta ingoiando l’errore?
- l’aggiornamento UI avviene sul MainActor?
Nei test e nei log, includi informazioni utili:
print("Loading product \(id)")
Meglio di:
print("here")
Debug di errori
Non trasformare errori in stringhe troppo presto.
catch {
self.errore = String(describing: error)
}
Va bene al confine UI, ma nel dominio mantieni errori tipizzati.
catch let errore as ErroreCoupon {
gestisci(errore)
}
Gli errori tipizzati sono più debuggabili.
Debug memoria
Per memory leak:
- aggiungi temporaneamente
deinit; - usa Memory Graph in Xcode;
- usa Instruments per casi seri;
- cerca closure che catturano
self; - cerca riferimenti forti reciproci.
deinit {
print("ProductViewModel deinit")
}
Se non appare, chiediti chi mantiene vivo l’oggetto.
Logging
print è utile in laboratorio. In codice professionale, valuta logging strutturato.
Un log utile contiene:
- evento;
- id;
- stato;
- errore;
- contesto minimo.
Evita log rumorosi che non aiutano a decidere.
Checklist debug
Quando hai un bug:
- riproducilo;
- formula ipotesi;
- scegli un punto di osservazione;
- usa breakpoint o test;
- riduci il caso;
- correggi;
- aggiungi test se il bug poteva regredire.
Esercizio
Prendi il laboratorio del client API concorrente e inserisci un bug:
isLoadingresta true dopo cancellazione;- un errore viene mostrato anche se è
CancellationError; - un coupon non valido ritorna totale invariato senza errore.
Debugga con breakpoint o test. Poi scrivi una breve nota: qual era l’ipotesi e come l’hai verificata?