Səhvlərin yayılması: bu nədir, səhvlərin yayılma mexanizmi və mobil inkişafda necə işləyir

Müəllif: IT Sectr Dərc olunub: 2026-05-26 Oxuma vaxtı: 9 dəq

Səhvlərin yayılması — səhvin yarandığı yerdən idarəediciyə qədər çağrılar yığınında yuxarıya doğru yayılma mexanizmi. Funksiya səhvi müstəqil idarə edə bilmədikdə, onu istisna (exception), throws deklarasiyası və ya qaytarılma növü vasitəsilə çağıran tərəfə ötürür. Səhvlərin düzgün yayılması mobil tətbiqlərin sabitliyi üçün vacibdir: işlənməmiş və ya səhv ötürülmüş səhvlər çökmələrə səbəb olur. Apple Swift Documentation (2026)-ya görə, Swift-də throws vasitəsilə avtomatik yayılma səhvi boilerplate kodu olmadan istənilən səviyyəyə ötürməyə imkan verir.

Əsas məqamlar

  • Səhvlərin yayılması — səhvin yarandığı yerdən yığın boyunca idarəediciyə, aralıq funksiyaları keçərək ötürülməsi
  • Swift-də throws vasitəsilə avtomatik yayılma səhvi yığının hər səviyyəsində açıq kod olmadan ötürür
  • Kotlin və Dart-da əl ilə yayılma hər səviyyədə açıq try-catch və ya Result konteynerində ötürmə tələb edir
  • Java-da yoxlanılmış istisnalar imzada throws vasitəsilə yayılmanı məcbur edir, yoxlanılmamışlar — görməməzliyə vurmağa imkan verir
  • Result Tipi — səhvin yığını açmadan dəyər kimi ötürüldüyü istisnalara alternativ

Səhvlərin yayılması nədir?

Səhvlərin yayılması — səhv obyektinin yarandığı funksiyadan çağrılar zənciri boyunca ən yaxın uyğun idarəediciyə qədər ötürülməsi prosesidir. Çağrılar yığınını təsəvvür edin: ViewController ViewModel-i, ViewModel Repository-ni, Repository API-ni çağırır. Əgər API şəbəkə xətası qaytarırsa, o, Repository və ViewModel vasitəsilə istifadəçiyə mesaj göstərəcək ViewController-a çatmalıdır. Hər bir aralıq funksiya qərar verir: səhvi idarə etmək və ya daha da ötürmək (yaymaq).

Yayılmanın iki yanaşması var: avtomatik və əl ilə. Avtomatik yanaşmada (Swift throws, Java yoxlanılmış istisnalar) kompilyator proqramçını ya səhvi idarə etməyə, ya da imzada yayılmanı bəyan etməyə məcbur edir. Əl ilə yanaşmada (Result Tipi, Kotlin Try) səhv dəyər kimi ötürülür — proqramçı səhvin ötürülməsi və ya transformasiyası üçün açıq kod yazır. Kotlin Result Docs (2026)-ya görə, Kotlin-də Result<T> birbaşa funksiya sərhədləri arasında yayılma üçün nəzərdə tutulmayıb — onu hər səviyyədə transformasiya etmək və ya idarə etmək lazımdır, bu da yayılmanı daha şüurlu, eyni zamanda daha uzun edir.

Yanaşma seçimi tətbiqin arxitekturasından və dildən asılıdır. Swift-də throws vasitəsilə avtomatik yayılma üstünlük təşkil edir, Kotlin-də — istisnaların (gözlənilməz səhvlər üçün) və Result tipli konteynerlərin (gözlənilən səhvlər üçün) qarışığı. Anlamaq vacibdir: yayılma məqsəd deyil, zərurətdir. İdeal arxitektura qərar qəbul etmək üçün kifayət qədər kontekstin olduğu ən aşağı səviyyədə səhvləri idarə edərək yayılma dərinliyini minimuma endirir.

Swift-də Throws vasitəsilə yayılma

Swift-də throws vasitəsilə yayılma avtomatik baş verir: əgər throws ilə A funksiyası throws ilə B funksiyasını çağırırsa və A do-catch-də B-nin səhvini idarə etmirsə, səhv avtomatik olaraq A-nı çağıran tərəfə ötürülür. Bu, Java yoxlanılmış istisnaları üçün xarakterik olan boilerplate kodunu aradan qaldırır, burada throws zəncirin hər metodunda bəyan edilməlidir. Swift prinsipi tətbiq edir: „zəncirdə bir throws funksiyası = aralıq səviyyələrdə idarə edilməzsə, bütün zəncir throws olur”.

swift
struct UserRepository {
    func fetchUser(id: Int) throws -> User {
        let data = try networkService.request(path: "/users/\(id)")
        return try parseUser(from: data)
    }
}

class UserViewModel {
    let repo = UserRepository()

    func loadUser(id: Int) throws -> User {
        return try repo.fetchUser(id: id)
    }
}

// ViewController — son idarəedici
func onButtonTap() {
    let vm = UserViewModel()
    do {
        let user = try vm.loadUser(id: 42)
        updateUI(user)
    } catch {
        showError("İstifadəçi yüklənə bilmədi")
    }
}

Yayılma zənciri: networkService.request -> fetchUser -> loadUser -> onButtonTap. Hər bir aralıq funksiya throws ilə işarələnib və do-catch ehtiva etmir — səhv avtomatik yuxarı ötürülür. ViewController onButtonTap do-catch ilə son idarəedicidir. Əgər ViewModel səhvi transformasiya etmək (başqa tipə bükmək) qərarı versəydi, do-catch və yeni throw istifadə edə bilərdi. Avtomatik yayılma kodu qısaldır: Repository səhvi necə idarə edəcəyini bilməli deyil — bu, istifadəçiyə mesaj göstərmək üçün UI-ya çıxışı olan ViewController-ın məsuliyyətidir.

Kotlin-də istisnalar vasitəsilə yayılma

Kotlin-də istisnalar vasitəsilə yayılma imzada throws bəyannaməsi tələb etmir (bütün istisnalar yoxlanılmamışdır). İstisna try-catch-ə rast gələnə qədər yığın boyunca avtomatik qalxır. Bununla belə, imzada throws-un olmaması yayılmanı qeyri-aşkar edir: proqramçı funksiyanın imzasından onun istisna ata biləcəyini görmür. Bu həm üstünlük (az boilerplate), həm də çatışmazlıqdır (idarə etməyi unutmaq asandır). Kotlin bu problemi dil vasitəsilə deyil, konvensiyalar və arxitektura nümunələri vasitəsilə həll edir.

kotlin
class UserRepository(
    private val api: ApiService,
    private val db: Database
) {
    suspend fun getUser(id: String): User {
        return try {
            api.fetchUser(id)
        } catch (e: IOException) {
            db.getCachedUser(id) ?: throw AppException("User unavailable")
        }
    }
}

class UserViewModel(private val repo: UserRepository) {
    private val _state = MutableStateFlow<UiState<User>>(UiState.Loading)
    val state: StateFlow<UiState<User>> = _state

    fun loadUser(id: String) {
        viewModelScope.launch {
            try {
                val user = repo.getUser(id)
                _state.value = UiState.Success(user)
            } catch (e: AppException) {
                _state.value = UiState.Error(e.message ?: "Unknown")
            }
        }
    }
}

Repository-də transformasiya ilə yayılma: IOException (şəbəkə əlçatan deyil) zamanı funksiya verilənlər bazasından keşlənmiş məlumatları almağa çalışır. Keş boşdursa, AppException atır — yayılma yeni səhv növü ilə davam edir. ViewModel AppException-ı tutur və UiState.Error-ə çevirir — səhv daha irəli getmir, yayılma UI qatında tamamlanır. Kotlin Coroutines xüsusiyyətlər əlavə edir: launch-dəki istisnalar avtomatik olaraq CoroutineExceptionHandler vasitəsilə yayılır, async-də isə yalnız await() çağırışında. SupervisorJob korutinalarda yayılmanı layihələndirərkən bunu nəzərə almaq vacibdir — SupervisorJob valideyn korutinanın ləğvinin qarşısını alır.

Result Tipi vasitəsilə yayılma

İstisnalara alternativ — uğur və ya səhvi dəyər kimi ötürən tip-konteyner vasitəsilə yayılma. Bu yanaşmada funksiya dəyəri deyil, bükümü qaytarır: Swift-də Result<T, E>, Kotlin-də Result<T>, Dart-da Either<L, R> (fpdart və ya dartz paketindən). Səhv yığını açmır — sadəcə konteynerdə yerləşir və növbəti səviyyə onunla nə edəcəyinə qərar verir. Bu, yayılmanı daha aşkar və idarə olunan edir.

kotlin
data class HttpResult<out T>(
    val data: T?,
    val error: AppError?
) {
    val isSuccess: Boolean get() = data != null
    val isError: Boolean get() = error != null
}

sealed class AppError {
    data class Network(val message: String) : AppError()
    data class Auth(val message: String) : AppError()
}

fun fetchUser(id: String): HttpResult<User> {
    return try {
        val response = api.get("/users/$id")
        HttpResult(data = parseUser(response), error = null)
    } catch (e: IOException) {
        HttpResult(data = null, error = AppError.Network("No internet"))
    }
}

HttpResult<T> — data və error sahələri olan sadə konteyner. Sealed class AppError səhv növlərini (Network, Auth) müəyyən edir. fetchUser funksiyası HttpResult qaytarır, yayılma yığının açılmasını tələb etmir — çağıran tərəf sadəcə isSuccess/isError-ı yoxlayır. Bu yanaşma xüsusilə Təmiz Arxitekturada faydalıdır, burada hər bir qat (data, domain, presentation) səhvi transformasiya edə bilər: IOError -> DomainError -> UiError. Konteyner vasitəsilə yayılma bu transformasiyaları aşkar və test edilə bilən edir, istisnalardan fərqli olaraq, burada çevrilmə zənciri funksiya imzalarında görünmür.

Yayılma və İdarəetmə: nə vaxt ötürmək, nə vaxt idarə etmək

Səhvlərin idarə edilməsinin layihələndirilməsində əsas qərarlardan biri yayılma (yuxarı ötürmək) və idarəetmə (burada idarə etmək) arasında seçimdir. Qərar qəbul etmə qaydası: səhvi mənalı hərəkət üçün kifayət qədər kontekstin olduğu səviyyədə idarə et. UI-ya girişin varsa — istifadəçiyə mesaj göstər. Keşə girişin varsa — bərpa etməyə çalış. Nə biri, nə də digəri yoxdursa — yay.

SsenariHərəkətƏsaslandırma
Repository-də şəbəkə xətasıYayRepository istifadəçinin sorğunu təkrarlamaq istədiyini bilmir
Repository-də analiz xətasıİdarə et (standart dəyər qaytar)Repository formatı bilir, ehtiyat dəyər qaytara bilər
ViewModel-də vaxt aşımıİdarə et (UiState.Error)ViewModel UiState-i idarə edir, səhvi necə çevirəcəyini bilir
Interceptor-da avtorizasiya xətasıİdarə et (tokeni yenilə)Interceptor tokenlərə çıxışı var və sessiyanı bərpa edə bilər
UseCase-də naməlum xətaYayUseCase-in UI konteksti yoxdur — yalnız biznes məntiqi

Qızıl qayda: aşağı səviyyələrdə minimum yayılma, maksimum idarəetmə. Əgər Repository keşdən bərpa edə bilirsə — səhvi yuxarı ötürmədən bunu etməlidir. Əgər ViewModel Snackbar göstərə bilirsə — qoy göstərsin, ViewController-dan əlavə kod tələb etmədən. Hər bir yayılma səviyyəsi bağlılığı artırır və test etməyi çətinləşdirir. Google Android Architecture Guide (2026)-ya görə, ViewModel səviyyəsində bütün mümkün vəziyyətləri (Loading, Success, Error) təmsil etmək üçün sealed class UiState istifadə edərək qat sərhədləri arasında yayılmanı minimuma endirmək və istisnaları birbaşa UI qatına ötürməmək tövsiyə olunur.

Səhvlərin yayılması problemləri və anti-nümunələri

Yanlış yayılma mobil tətbiqlərdə çətin aşkar edilən səhvlərin mənbəyidir. Proqramçıların qarşılaşdığı beş əsas problemi və onların həll yollarını nəzərdən keçirək.

Səhv kontekstinin itirilməsi

Ən çox yayılmış problem: yayılma zamanı istisna tutulur, jurnala yazılır və orijinal istisna olmadan yenisi atılır. Proqramçı StackTrace-i itirir və səhvin harada baş verdiyini anlaya bilmir. Swift-də error chaining istifadə edin: throw MyError(context: originalError). Kotlin-də: throw AppException(cause = originalException). Dart-da: throw AppException(message, originalException). Heç vaxt səbəbi/əsas səhvi ötürmədən yeni istisna yaratmayın.

Səhvi görməməzliyə vurmaq (boş catch)

catch (e: Exception) { /* heç nə */ } — tətbiqin yanlış vəziyyətdə işləməyə davam etməsinə səbəb olan anti-nümunə. Səhvi görməməzliyə vura biləcəyinizə əminsinizsə — əsaslandırma ilə şərh əlavə edin. Swift-də seçmə görməməzliyə vurmaq üçün try? istifadə edin (səhv -> nil). Kotlin-də — Result<T>.onFailure { /* log */ }. Jurnala yazmadan istisnaları boğmayın.

Həddindən artıq yayılma dərinliyi

Səhv idarə edilmədən 5+ səviyyədən keçirsə, arxitektura yenidən nəzərdən keçirilməlidir. Hər bir yayılma səviyyəsi aşağıdakı funksiyaların throws imzasından asılılıqdır. Həll: qat sərhədlərində Failure konteynerlərindən (sealed class Result { Success, Error }) istifadə edin ki, yayılma aşkar və məhdud olsun. Yayılma zənciri nə qədər qısa olsa, kodu test etmək və sazlamaq bir o qədər asandır.

SupervisorJob olmadan korutinalarda yayılma

Kotlin Coroutines-də launch-dəki istisna standart olaraq valideyn korutinanı və bütün siblings-i (eyni scope-un uşaqları) ləğv edir. 10 paralel tapşırıqdan biri düşərsə, qalan 9-u ləğv ediləcək, bu çox vaxt arzuolunmazdır. Səhvləri izolyasiya etmək üçün SupervisorJob və ya supervisorScope istifadə edin: bir uşaqdakı səhv siblings-i ləğv etmir. ViewModelScope standart olaraq SupervisorJob istifadə edir, bu da Android-də bu problemdən qoruyur.

İdarəetməsiz callback vasitəsilə yayılma

Callback əsaslı API-də səhv tez-tez callback parametri kimi ötürülür. Əgər callback səhvi idarə etmirsə (və ya yanlış idarə edirsə), yayılma qeyri-aşkar olur və asanlıqla itir. Həll: async/await (Swift) və ya korutinalara (Kotlin) keçin, burada yayılma standart try-catch mexanizmləri vasitəsilə işləyir. Əgər callback qaçılmazdırsa — hər iki halın məcburi idarə edilməsi üçün Either<Error, T> və ya Result<T> istifadə edin.

Tez-tez verilən suallar

Səhvlərin yayılması throw-dan nə ilə fərqlənir?

Throw — istisnanın bir dəfəlik atılması hərəkətidir. Səhvlərin yayılması — səhvin throw-dan catch-ə qədər bir neçə yığın səviyyəsi vasitəsilə ötürülməsinin bütün prosesidir. Yayılma throw-u, aralıq funksiyalar vasitəsilə avtomatik və ya əl ilə ötürməni və son idarəetməni əhatə edir. Bu, səhvin həyat dövrünü təsvir edən daha geniş anlayışdır.

Səhvlərin yayılmasını necə test etmək olar?

Verilən ssenarilərdə istisnalar atan mock obyektlərindən istifadə edin. Funksiyanın səhvi düzgün yaydığını və ya idarə etdiyini assertThrows (Kotlin/JUnit) və ya XCTAssertThrowsError (Swift/XCTest) vasitəsilə yoxlayın. Result əsaslı yayılma üçün hər iki halda isSuccess/isError və dəyərləri yoxlayın.

Nə vaxt Result vasitəsilə yayılma istisnalardan daha yaxşıdır?

Result yayılması bir arxitektura sərhədi daxilində gözlənilən səhvlər (yanlış məlumatlar, biznes qaydaları) üçün üstünlük təşkil edir. İstisnalar yüksək səviyyədə idarə edilməli olan gözlənilməz səhvlər (şəbəkə itkisi, giriş-çıxış səhvləri) üçün daha yaxşıdır. Səhv ilə nəticə icra axınını kəsmir, istisna isə kəsir.

Kotlin korutinaları vasitəsilə səhvi necə yaymaq olar?

Kotlin Coroutines-də launch-dəki istisna avtomatik olaraq siblings-in ləğvi ilə CoroutineScope vasitəsilə yayılır. İzolyasiya üçün supervisorScope və ya SupervisorJob istifadə edin: bir korutinadakı səhv digərlərini ləğv etmir. Async üçün səhv await() çağırışında try-catch vasitəsilə açıq şəkildə idarə edilməlidir, əks halda udulacaq.

Hansı səviyyə səhvin son idarəedicisi olmalıdır?

İdeal son idarəedici UI qatıdır (ViewController, Fragment/Composable). Yalnız o, istifadəçi interfeysinə çıxışa malikdir və mesaj, Snackbar və ya dialoq göstərə bilər. Aralıq qatlar (Repository, UseCase, ViewModel) səhvi yayır, lazım gəldikdə onu daha abstrakt domen tipinə çevirir.

Nəticə

  • Səhvlərin yayılması — səhvin yarandığı yerdən idarəediciyə qədər istisnalar və ya Result konteynerləri vasitəsilə yığın boyunca ötürülməsi
  • Swift-də throws vasitəsilə avtomatik yayılma aralıq səviyyələrdə kod tələb etmir — səhv özü qalxır
  • Kotlin-də əl ilə yayılma hər səviyyədə açıq try-catch və throw vasitəsilə idarəetməni şüurlu, lakin uzun edir
  • Result Tipi səhvi yığını açmadan dəyər kimi ötürür, Təmiz Arxitekturada gözlənilən səhvlər üçün əlverişlidir
  • İdarəetmə qaydası: kontekst olan səviyyədə idarə et (UI), kontekst olmayan səviyyələrdən yay (domain, data)
  • Anti-nümunələr: boş catch, transformasiya zamanı səbəbin itirilməsi, həddindən artıq yayılma dərinliyi, SupervisorJob-un görməməzliyə vurulması
  • Hər qat üçün tipləşdirilmiş səhvlər (sealed class / enum) layihələndirin və qat sərhədlərini keçərkən onları transformasiya edin

Açar təslim mobil tətbiq hazırlayacağıq

IT Sectr 2017-ci ildən startaplar və bizneslər üçün iOS və Android tətbiqləri yaradır. Sizə məsləhət verəcəyik və ən yaxşı həlli təklif edəcəyik.

Layihəni müzakirə et

Həm də oxuyun