Try-Catch — o construcție de captare a excepțiilor care permite executarea codului potențial periculos într-un bloc protejat și gestionarea corectă a erorilor fără terminarea bruscă a programului. Blocul try conține cod care poate arunca o excepție, catch o captează și execută logica de recuperare. Conform Apple Swift Documentation (2026), blocul finally se execută indiferent dacă excepția a fost aruncată sau nu, garantând eliberarea resurselor.
Puncte Principale
Try-Catch — o construcție fundamentală a gestionării structurate a excepțiilor, prezentă în majoritatea limbajelor de programare moderne. Este formată din trei blocuri: try (încercarea de a executa cod periculos), catch (captarea și gestionarea excepției) și finally opțional (finalizarea). Ideea construcției este separarea logicii operațiunilor de afaceri de logica gestionării erorilor, făcând codul mai lizibil și mai predictibil.
Conceptul a fost implementat pentru prima dată în limbajul C++ ca try/catch, apoi preluat de Java, C#, Swift, Kotlin, Dart, Python, JavaScript și alte limbaje. Fiecare limbaj adaugă propriile caracteristici: în Swift blocul catch trebuie să fie exhaustiv, în Kotlin try-catch poate fi o expresie, în Dart finally este obligatoriu pentru resursele de flux. În ciuda diferențelor, principiul de bază este același: eroarea este gestionată cât mai aproape de locul apariției, nu la nivel global.
Utilizarea Try-Catch este deosebit de relevantă în dezvoltarea mobilă, unde factorii externi — pierderea rețelei, răspuns incorect al serverului, insuficiență de memorie — apar constant. Gestionarea corectă a excepțiilor previne crash-urile aplicației și asigură un UX adecvat: utilizatorul primește un mesaj de eroare în locul închiderii bruște a aplicației. Conform Google Android Kotlin Style Guide (2026), fiecare funcție care poate arunca o excepție trebuie fie să o gestioneze prin try-catch, fie să declare throws în semnătură.
Mecanismul de execuție al try-catch se bazează pe derularea stivei (stack unwinding). Când în interiorul blocului try este aruncată o excepție prin operatorul throw (sau ca urmare a unei erori de sistem), fluxul normal de execuție este întrerupt imediat. Execuția se deplasează la un nivel superior al stivei de apeluri în căutarea unui bloc catch potrivit. Limbajele moderne caută un catch cu tipul corespunzător tipului excepției aruncate, folosind mecanismul de potrivire a tipurilor (type matching).
Dacă este găsit catch-ul potrivit, corpul acestuia este executat, după care execuția continuă după întreaga construcție try-catch-finally. Dacă catch nu este găsit, excepția urcă mai departe în stivă și poate fi gestionată la un nivel superior — până la handler-ul global care în aplicația mobilă arată utilizatorului un dialog de eroare. Dacă excepția nu este gestionată nicăieri, aplicația se termină brusc. De aceea gestionarea corectă a tuturor tipurilor posibile de excepții este critică pentru stabilitatea aplicației.
fun readUserData(): User {
return try {
val response = api.fetchUser()
parseUser(response)
} catch (e: IOException) {
logError("Eroare de rețea", e)
throw AppException("Nu s-au putut încărca datele")
} catch (e: JsonParseException) {
logError("Eroare de parsare", e)
return User.default()
} finally {
closeLoadingIndicator()
}
}
Codul încearcă mai întâi să execute o cerere API și să parseze răspunsul. Dacă apare IOException (problemă de rețea), excepția este logată și propagată mai departe ca AppException. Dacă apare JsonParseException — este returnat utilizatorul implicit. Blocul finally ascunde garantat indicatorul de încărcare, prevenind scurgeri de componente UI pe ecran.
În Swift, gestionarea erorilor este implementată prin protocolul Error (anterior ErrorType). Orice tip conform cu Error poate fi aruncat prin operatorul throw. O funcție care poate arunca o eroare este marcată cu cuvântul-cheie throws în semnătură. Apelarea unei astfel de funcții necesită prefixul try (pentru try-catch explicit), try? (rezultat opțional) sau try! (execuție forțată fără gestionarea erorii).
enum NetworkError: Error {
case noConnection
case serverError(code: Int)
case timeout
}
func fetchUser(id: Int) throws -> User {
guard isConnected() else {
throw NetworkError.noConnection
}
let data = try performRequest(path: "/users/\(id)")
return try decodeUser(from: data)
}
do {
let user = try fetchUser(id: 42)
updateUI(user)
} catch NetworkError.noConnection {
showOfflineAlert()
} catch let error as NetworkError {
showError("Rețea " + error.localizedDescription)
} catch {
showGenericError()
}
Enum NetworkError implementează protocolul Error, definind trei cazuri: noConnection, serverError cu cod și timeout. Funcția fetchUser este marcată cu throws: mai întâi verifică conexiunea, apoi execută cererea și parsarea. În blocul do-catch, trei catch gestionează scenarii diferite: cazul specific noConnection, tipul general NetworkError și toate celelalte erori. Acest lucru permite afișarea de mesaje diferite utilizatorului în funcție de tipul problemei.
Kotlin a moștenit try-catch-finally de la Java, dar a adăugat o diferență importantă: în Kotlin, try-catch este o expresie (expression), nu o instrucțiune (statement). Aceasta înseamnă că rezultatul blocului try sau al blocului catch poate fi atribuit unei variabile. Ultima expresie din blocul try devine rezultatul la succes, ultima expresie din catch — la eroare. Dacă eroarea nu este gestionată de niciun catch, excepția este propagată mai sus în stivă.
sealed class Result<out T> {
data class Success<out T>(val data: T) : Result<T>()
data class Error(val exception: Throwable) : Result<Nothing>()
}
fun loadData(): Result<List<Item>> {
return try {
val response = api.getItems()
Result.Success(response.toList())
} catch (e: HttpException) {
Log.e("HTTP ", e)
Result.Error(e)
} catch (e: IOException) {
Log.e("Rețea ", e)
Result.Error(e)
}
}
În exemplu, sealed class Result împachetează un răspuns de succes sau o eroare. Funcția loadData folosește try-catch ca expresie: la succes returnează Result.Success, la excepția HttpException sau IOException — Result.Error cu logare. Această abordare permite părții apelante să gestioneze erorile fără excepții — prin expresia when după tipul Result. Acest lucru este deosebit de convenabil în Jetpack Compose pentru afișarea diferitelor stări UI (Loading, Success, Error) prin StateFlow și collectAsState.
Dart suportă try-catch-finally cu o sintaxă similară cu Java, dar cu adăugarea clauzei on pentru filtrarea după tipul excepției fără a specifica o variabilă. Acest lucru este convenabil când excepția în sine nu este necesară — doar faptul tipului său este important. Dart suportă, de asemenea, blocul catch cu doi parametri: obiectul excepției și StackTrace, util pentru logarea întregului lanț de apeluri.
import 'dart:io';
import 'dart:convert';
class UserRepository {
Future<User> fetchUser(String id) async {
try {
final client = HttpClient();
final request = await client.getUrl(
Uri.parse('https://api.example.com/users/$id')
);
final response = await request.close();
final body = await response.transform(utf8.decoder).join();
return User.fromJson(json.decode(body));
} on SocketException catch (e, stackTrace) {
log("No internet", e, stackTrace);
throw AppException("Connection failed");
} on FormatException {
throw AppException("Invalid response format");
} finally {
client.close();
}
}
}
SocketException este capturată cu obiectul și StackTrace pentru logare detaliată, apoi propagată ca AppException. FormatException este capturată fără variabilă — este suficient să știm că formatul răspunsului este incorect. Blocul finally închide garantat HttpClient, prevenind scurgeri de socket. În Flutter, această abordare este deosebit de importantă pentru testarea Widget, unde excepțiile negestionate în State.initState duc la prăbușirea întregii sesiuni de testare.
Chiar și dezvoltatorii experimentați fac greșeli la lucrul cu try-catch care duc la scurgeri de memorie, bug-uri ascunse sau comportament neadecvat al aplicației. Să examinăm cinci probleme frecvente în dezvoltarea mobilă.
Catch-ul gol — una dintre cele mai proaste practici. Excepția este înghițită, aplicația continuă să funcționeze în stare incorectă, iar dezvoltatorul nu află despre problemă. Întotdeauna cel puțin logați excepția. În Kotlin folosiți catch(e: Exception) { Log.e(...) }, în Swift — catch { print($0) }. În Dart, catch-ul minim acceptabil trebuie să apeleze debugPrint sau să scrie în Crashlytics.
Capturarea tuturor excepțiilor prin catch (Exception e) fără diferențiere pe tipuri ascunde erori neașteptate — NullPointerException, OutOfMemoryError, StackOverflowError. Prindeți doar acele tipuri pe care le așteptați și le puteți gestiona. Pentru restul, permiteți propagarea în sus. În dezvoltarea mobilă, catch-uri specifice pentru IOException, TimeoutException, AuthException oferă mesaje mai semnificative utilizatorului.
Resursele — fișiere, socket-uri, cursoare BD, animații — trebuie eliberate în finally sau în blocul use (AutoCloseable). Dezvoltatorii uită adesea să închidă resursele la excepție, ceea ce duce la scurgeri. În Kotlin folosiți .use { } pentru resurse Closeable, în Swift — defer { }, în Dart — await using din pachetul async. Blocul finally garantează eliberarea chiar și la aruncarea unei excepții în interiorul catch.
În codul asincron, try-catch nu captează excepțiile din alte fire. În Kotlin Coroutines folosiți CoroutineExceptionHandler sau SupervisorJob. În Swift async/await — do-catch în interiorul Task. În Flutter — runZonedGuarded pentru capturarea globală. Ignorarea acestei reguli este cauza crash-urilor greu de reprodus în producție.
Gestionarea excepției nu trebuie să blocheze interfața utilizatorului pe o perioadă nedeterminată. Afișați utilizatorului un mesaj specific și oferiți posibilitatea de a repeta operația. Snackbar cu buton Retry în Kotlin/Compose, UIAlertController cu action în Swift, SnackBar cu action în Flutter — UX minim suficient pentru erori de rețea sau server. Evitați dialogurile generice „A apărut o eroare" fără posibilitate de recuperare.
Întrebări frecvente
Try-catch folosește excepții și derularea stivei pentru gestionarea erorilor, ceea ce poate fi costisitor din punct de vedere al performanței la un număr mare de erori. Result Type este un tip-container (Success sau Failure) gestionat prin pattern matching fără derularea stivei, mai eficient pentru erorile așteptate.
Finally este obligatoriu dacă blocul try deschide resurse (fișiere, socket-uri, cursoare) care trebuie închise. Dacă resursele nu sunt deschise, finally nu este necesar. În limbajele moderne folosiți AutoCloseable/use/defer pentru închiderea automată a resurselor fără finally. Blocul use în Kotlin și Swift înlocuiește finally pentru obiectele Closeable.
În fluxul normal (fără excepție), try-catch practic nu afectează performanța — JVM și compilatorul Swift optimizează acest caz. Dar la aruncarea unei excepții are loc derularea stivei, care poate dura 10–100 µs în funcție de adâncimea stivei. Nu folosiți excepțiile pentru controlul fluxului de execuție — este un antipattern.
În corutine folosiți try-catch în interiorul coroutineScope sau CoroutineExceptionHandler pentru capturarea globală. SupervisorJob previne anularea corutinei părinte la eroarea în corutina copil. Pentru launch folosiți CoroutineExceptionHandler, pentru async — try-catch în jurul await().
Catch-urile multiple sunt preferabile: codul se citește liniar, fiecare bloc gestionează un tip de excepție. Un singur catch cu if-else este mai greu de întreținut, se poate omite ușor un nou tip de excepție. În Swift, catch-urile multiple sunt obligatorii pentru gestionarea exhaustivă a enum Error, în Kotlin nu există restricții, dar cea mai bună practică este un catch separat per tip.
Rezumat
Vom dezvolta o aplicație mobilă la cheie
IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.
Citiți și