Fatal Error — είναι ένα κρίσιμο σφάλμα που οδηγεί σε άμεση διακοπή της λειτουργίας της εφαρμογής (crash). Σε αντίθεση με το non-fatal error, το θανατηφόρο σφάλμα δεν αφήνει στο πρόγραμα δυνατότητα ανάκτησης — η διεργασία τερματίζεται απότομα από το λειτουργικό σύστημα ή το περιβάλλον χρόνου εκτέλεσης (runtime). Σύμφωνα με τα δεδομένα Firebase Crashlytics 2024, η μέση εφαρμογή χάνει 2.5% των χρηστών μετά από κάθε crash, και η επίλυση των θανατηφόρων σφαλμάτων αποτελεί προτεραιότητα νούμερο ένα στην ανάπτυξη εφαρμογών κινητών. Όσο υψηλότερο είναι το crash-free rate, τόσο υψηλότερη η αβαθμολόγηση της εφαρμογής στα καταστήματα και τόσο μικρότερη η απορροή χρηστών.
Κύρια σημεία
Fatal Error — είναι ένα σφάλμα κατά το οποίο η περαιτέρω εκτέλεση του προγράμματος είναι αδύνατη. Το λειτουργικό σύστημα ή η εικονική μηχανή τερματίζει τη διεργασία για να αποτραπεί η ζημιά δεδομένων. Στο iOS, το θανατηφόρο σφάλμα προκαλεί σήμα SIGABRT ή SIGSEGV, στο Android — μη επεξεργασμένη εξαίρεση που φτάνει στον ριζικό χειριστή και τερματίζει τη διεργασία. Η εφαρμογή κλείνει αμέσως και ο χρήστης επιστρέφει στην Αρχική Οθόνη.
Τα χαρακτηριστικά σημεία ενός θανατηφόρου σφάλματος: crash report με πλήρες stack trace, απροσδόκητη εξαφάνιση της εφαρμογής, εγγραφή στο system log για τερματισμό της διεργασίας, μαύρη ή άσπρη οθόνη πριν το κλείσιμο. Ο χρήστης βλέπει την Αρχική Οθόνη χωρίς δυνατότητα αποκατάστασης της συνεδρίας — η εφαρμογή πρέπει να επανεκκινηθεί από την αρχή. Στο iOS, το crash συνοδεύεται από εγγραφή σε αρχείο .crash, προσβάσιμο μέσω Xcode Organizer.
Κάθε crash επηρεάζει αρνητικά το user retention. Σύμφωνα με το Google Play Console 2024, εφαρμογές με crash-free rate κάτω από 99.5% λαμβάνουν μειωμένη βαθμολογία στην αναζήτηση και στις συστάσεις. Το crash-rate είναι ένα από τα κλειδικά σήματα ποιότητας για το App Store και το Google Play — το υψηλό επίπεδο θανατηφόρων σφαλμάτων μπορεί να αποκλείσει την δημοσίευση ενημερώσεων. Για χρηματοοικονομικές και ιατρικές εφαρμογές, το crash-free rate κάτω από 99.9% θεωρείται απαράδεκτο.
Null-pointer dereference — η κυριάρχει αιτία θανατηφόρων σφαλμάτων σε εφαρμογές κινητών. Η προσπάθεια πρόσβασης σε ιδιότητα ή μέθοδο ενός αντικειμένου που είναι null προκαλεί NullPointerException στο Android ή EXC_BAD_ACCESS στο iOS. Σύμφωνα με τα δεδομένα JetBrains 2023, περίπου 28% όλων των production crashes σχετίζονται με null pointers. Στο Kotlin, το σύστημα null-safety μειώνει σημαντικά αυτό το ποσοστό, αλλά το force unwrap και η συμβατότητα με Java παραμένουν πηγές προβλήματος.
Πρόσβαση σε στοιχείο συλλογής με μη υπάρχον ευρετήριο — η δεύτερη συχνότερη αιτία crashes. Στη Java και στο Kotlin είναι ArrayIndexOutOfBoundsException, στο Swift — fatal error: Index out of range. Συνήθως προκύπτει κατά την εργασία με λίστες μετά από φιλτράρισμα ή δυναμική αλλαγή του μεγέθους της συλλογής. Η χρήση ασφαλών μεθόδων όπως getOrNull (Kotlin) ή indices.contains (Swift) αποτρέπει αυτόν τον τύπο θανατηφόρων σφαλμάτων.
Έλλειψη μνήμης (OutOfMemoryError), υπερχείλιση στίβας (StackOverflowError), φόρτωση μη υπάρχοντος πόρου — resource σφάλματα συχνά είναι θανατηφόρα και δύσκολο να αναπαραχθούν. Το OutOfMemoryError προκύπτει κατά τη φόρτωση μεγάλων εικόνων χωρίς συμπίεση ή λόγω διαρροής μνήμης από μη απελευθερωμένες αναφορές. Το StackOverflowError προκύπτει από βαθεία αναδρομή χωρίς βασική περίπτωση ή από κυκλικές κλήσεις σε αλυσίδες εντολών.
Deadlock, race condition, τροποποίηση συλλογής κατά τη διάρκεια της επανάληψης — σφάλματα πολλαπλών νημάτων εκδηλώνονται μη ντετερμινιστικά και είναι τα πιο δύσκολα στη διάγνωση. Στο Android, ConcurrentModificationException κατά τροποποίηση ArrayList από διαφορετικά νήματα, στο iOS crash από τροποποίηση NSMutableArray χωρίς συγχρονισμό. Η χρήση coroutines Kotlin (structured concurrency) ή Swift Actors (iOS 16+) μειώνει την πιθανότητα concurrency crashes.
Η κλειδική διαφορά είναι η δυνατότητα ανάκτησης. Το Non-Fatal Error επιτρέπει στο πρόγραμα να συνεχίσει τη λειτουργία του: το χρονικό όριο δικτύου επεξεργάζεται με try-catch, το σφάλμα ανάλυσης αντικαθίσταται με προκαθορισμένη τιμή. Το Fatal Error δεν έχει τέτοια διαδρομή — το crash είναι αναπόφευκτο και η εφαρμογή πρέπει να επανεκκινηθεί. Το όριο μεταξύ αυτών των δύο τύπων σφαλμάτων καθορίζεται από την αρχιτεκτονική της εφαρμογής.
| Χαρακτηριστικό | Fatal Error | Non-Fatal Error |
|---|---|---|
| Τερματισμός εφαρμογής | Ναι | Όχι |
| Ανάκτηση | Αδύνατη | Επιτρεπτή μέσω catch-block |
| Συλλογή πληροφοριών | Μόνο crash reporter | Καταγραφή από τον κώδικα |
| Ζημία UX | Πλήρης αποτυχία συνεδρίας | Προσωρινή ενόχληση |
| Τυπικό παράδειγμα | NullPointerException | IOException |
Το ίδιο σφάλμα μπορεί να είναι fatal σε μία πλατφόρμα και non-fatal σε μία άλλη. Η διαίρεση διά του μηδενός σε Java/Kotlin προκαλεί ArithmeticException (μη θανατηφόρο — μπορεί να πιαστεί), στο Swift προκαλεί fatal error: Division by zero (crash χωρίς δυνατότητα πίασης). Ο αναπτυκτής πρέπει να λαμβάνει υπόψη τη συμπεριφορά της συγκεκριμένης γλώσσας και του περιβάλλοντος χρόνου εκτέλεσης κατά τον σχεδιασμό χειρισμού σφαλμάτων. Η κατανόηση του ορίου μεταξύ fatal και non-fatal αποτελεί τη βάση για την ανοχή σφαλμάτων στην αρχιτεκτονική εφαρμογών κινητών.
Firebase Crashlytics — το πρότυπο de facto για τη διάγνωση crashes σε εφαρμογές κινητών. Το SDK συλλέγει αυτόματα το stack trace, την κατάσταση της συσκευής, την έκδοση ΛΟΣ και τα αρχεία καταγραφής αμέσως πριν από το crash. Το Dashboard ομαδοποιεί τα παρόμοια crashes σε μία αντιμετώπιση, εμφανίζοντας τον αριθμό των επηρεασμένων χρηστών, τη συχνότητα εμφάνισης και την έκδοση της εφαρμογής στην οποία συνέβη το crash.
// Αρχικοποίηση Crashlytics σε εφαρμογή Android
class MainApplication : Application() {
override fun onCreate() {
super.onCreate()
FirebaseApp.initializeApp(this)
Crashlytics.setCustomKey("build_type", "production")
}
}
// Ορισμός προσαρμοσμένων δεδομένων χρήστη για διαγνωστικά crash
Crashlytics.setUserId("user_12345")
Crashlytics.setCustomKey("screen", "ProfileFragment")
Crashlytics.setCustomKey("api_response", responseCode)
// Αναγκαστικό crash για δοκιμή ενσωμάτωσης
Crashlytics.crash()
Sentry — εναλλακτική λύση με πιο λεπτομερή διάγνωση. Το Sentry δείχνει όχι μόνο το stack trace, αλλά και την κατάσταση όλων των μεταβλητών, την ακολουθία γεγονότων πριν από το σφάλμα και το περιεχόμενο εκτέλεσης. Τα Breadcrumbs του Sentry επιτρέπουν την ανασύνταξη της αλυσίδας ενεργειών του χρήστη πριν από το θανατηφόρο σφάλμα: πατήματα κουμπιών, μεταβάσεις μεταξύ οθονών, αιτήσεις δικτύου. Στο Sentry διατίθεται παρακολούθηση απόδοσης και συνεδριών για ολοκληρωμένη ανάλυση ποιότητας.
Για τη σωστή διάγνωση crashes στο iOS απαιτείται η φόρτωση dSYM αρχείων (debug symbols) στο Crashlytics ή Sentry. Χωρίς dSYM, το stack trace θα περιέχει μόνο διευθύνσεις μνήμης αντί για ονόματα συναρτήσεων. Για Android απαιτείται η φόρτωση mapping αρχείων κατά τη χρήση ProGuard ή R8. Η αυτοματοποίηση φόρτωσης dSYM μέσω build phase στο Xcode ή Gradle plugin είναι υποχρεωτική για τις production εκδόσεις.
Βασική μέθοδος πρόληψης — το safe unwrapping όλων των προαιρετικών και nullable τιμών. Η χρήση if-let στο Swift και let με ?: στο Kotlin εξαλείφει τα σφάλματα null-pointer. Καμία force unwrap χωρίς εγγύηση υπαρξης της τιμής. Τος συμπεριφτατής Kotlin και Swift προειδοποιούν για δυνητικά επικίνδυνες λειτουργίες — αυτές οι προειδοποιήσεις δεν πρέπει να αγνοούνται σε production κώδικα.
// ΠΡΟΛΗΨΗ fatal error μέσω safe unwrapping
func processUser(id: String) -> String {
guard let user = database.findUser(by: id) else {
return "User not found"
}
guard let email = user.email else {
return "Email not set"
}
return email
}
// Ασφαλής πρόσβαση σε στοιχεία συλλογής
func safeGet <T>(items: [T], index: Int) -> T? {
guard items.indices.contains(index) else { return nil }
return items[index]
}
// Έλεγχος ορίων πίνακα πριν από την πρόσβαση
let numbers = [1, 2, 3]
if numbers.indices.contains(5) {
print(numbers[5])
} else {
print("Index out of range")
}
Defensive programming — το δεύτερο επίπεδο προστασίας. Ελέγχετε πάντα τις εισαγωγες παραμέτρους των συναρτήσεων, επιστρέφετε Optional ή Result αντί για force unwrap, χρησιμοποιείτε assert σε debug εκδόσεις για έγκαιρη ανίχνευση σφαλμάτων κατά τη φάση ανάπτυξης. Τα Unit tests σε οριακές περιπτώσεις (null, κενές συλλογές, λανθασμένα ευρετήρια) πρέπει να καλύπτουν όλα τα δημόσια σημεία εισόδου στην επιχειρηματική λογική της εφαρμογής.
Σε React Native και SwiftUI μπορείτε να ορίσετε ένα error boundary — ένα στοιχείο που πιάνει θανατηφόρα σφάλματα απόδοσης και εμφανίζει fallback UI αντί για crash. Αυτό μετατρέπει ένα θανατηφόρο UI σφάλμα σε non-fatal από την άποψη του χρήστη — η εφαρμογή συνεχίζει να λειτουργεί, και ο χρήστης βλέπει ένα μήνυμα σφάλματος σε ένα συγκεκριμένο τμήμα της διεπαφής, όχι μια άσπρη οθόνη.
Ενσωμάτωση αυτόματων ελέγχων στο CI/CD pipeline: στατική ανάλυση (Detekt για Kotlin, SwiftLint για Swift), εκτέλεση UI tests σε πραγματικές συσκευές, έλεγχος crash-free rate σε δοκιμαστικό περιβάλλον. Παραγγελία συγχώνευσης κατά την υπέρβαση του ορίου crash-rate (συνιστώμενο όριο — παραπάνω 0.1% νέων crashes ανά commit).
Συχνές ερωτήσεις
Όχι, μετά από fatal error η ανάκτηση είναι αδύνατη — η διεργασία τερματίζεται σε επίπεδο ΛΟΣ. Ο μόνος τρόπος είναι να αποτραπεί το θανατηφόρο σφάλμα πριν εμφανιστεί μέσω ασφαλών κατασκευών, defensive programming και επισταμένης δοκιμής οριακών περιπτώσεων κατά τη φάση ανάπτυξης.
Segfault (SIGSEGV) — είναι ένας τύπος fatal error, που προκύπτει κατά την πρόσβαση σε μη επιτρεπόμενη περιοχή μνήμης. FATAL ERROR είναι η γενική έννοια για όλα τα μη ανακτήσιμα σφάλματα, συμπεριλαμβανομένων segfault, abort, stack overflow, out of memory και μη επεξεργασμένων εξαιρέσεων κατά τη συνέχεια.
Η ενσωμάτωση Crashlytics (Firebase) ή Sentry SDK συλλέγει αυτόματα όλες τις μη επεξεργασμένες εξαιρέσεις. Το SDK πιάνει σήματα ΛΟΣ και εξαιρέσεις χρόνου εκτέλεσης, δημιουργεί αναφορά crash με stack trace και περιεχόμενο και την αποστέλλει στον διακομιστή κατά την επόμενη εκκίνηση της εφαρμογής.
Για τη δοκιμή χειρισμού crashes χρησιμοποιείται force crash σε debug έκδοση. Το Crashlytics παρέχει τη μέθοδο crash() για την εξομοίωση ενός θανατηφόρου σφάλματος. Σε unit tests ελέγχεται η ορθότητα guard και if-let, ενώ UI tests καλύπτουν οριακές περιπτώσεις εισαγωγής δεδομένων και κατάστασης διεπαφής.
Όχι, μόνο οι μη επεξεργασμένες εξαιρέσεις γίνονται θανατηφόρες. Μία εξαίρεση που πιάνεται από try-catch είναι non-fatal. Η διαφορά ανάμεσα σε μια επεξεργασμένη και μη επεξεργασμένη εξαίρεση καθορίζει αν η εφαρμογή θα τερματιστεί ή θα συνεχίσει με εναλλακτική κατάσταση με ελάχιστη ζημία για την εμπειρία του χρήστη.
Σύνοψη
Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση
Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης