Τι είναι οι κύκλοι διατήρησης (retain cycles) στο iOS;
sobes.tech AI
Απάντηση από AI
Οι κύκλοι διατήρησης προκύπτουν όταν δύο ή περισσότεροι αντικείμενα έχουν ισχυρές αναφορές το ένα στο άλλο, δημιουργώντας έναν "κλειστό κύκλο". Ως αποτέλεσμα, ο μετρητής αναφορών κάθε αντικειμένου ποτέ δεν φτάνει το μηδέν και δεν μπορεί να απελευθερωθεί από το ARC (Automatic Reference Counting). Αυτό οδηγεί σε διαρροές μνήμης, καθώς τα αντικείμενα παραμένουν στη μνήμη ακόμα και μετά το τέλος της χρήσης τους.
Παράδειγμα:
class Person {
let name: String
var apartment: Apartment?
init(name: String) {
self.name = name
print("\(name) αρχικοποιείται")
}
deinit {
print("\(name) απελευθερώνεται")
}
}
class Apartment {
let unit: String
var tenant: Person?
init(unit: String) {
self.unit = unit
print("Apartment \(unit) αρχικοποιείται")
}
deinit {
print("Apartment \(unit) απελευθερώνεται")
}
}
Σε αυτό το παράδειγμα, αν το Person έχει ισχυρή αναφορά στο Apartment και το Apartment έχει ισχυρή αναφορά στο Person, τότε κατά την προσπάθεια απελευθέρωσης αυτών των αντικειμένων, δεν θα διαγραφούν, καθώς οι μετρητές αναφορών τους δεν φτάνουν το μηδέν.
Πώς να λύσετε το πρόβλημα των κύκλων διατήρησης:
Χρησιμοποιήστε ασθενείς (weak) ή αδέσμευτες (unowned) αναφορές.
weak: Χρησιμοποιείται όταν είναι πιθανό το συσχετιζόμενο αντικείμενο να γίνειnilκατά τη διάρκεια του κύκλου ζωής ενός άλλου αντικειμένου. Η αναφορά γίνεται αυτόματαnilόταν το αντικείμενο στο οποίο αναφέρεται απελευθερώνεται. Είναι προαιρετικό.unowned: Χρησιμοποιείται όταν είναι γνωστό ότι το συσχετιζόμενο αντικείμενο έχει τον ίδιο ή μεγαλύτερο κύκλο ζωής από το αντικείμενο που έχει την αναφορά. Δεν μπορεί να είναιnil.
Διορθωμένο παράδειγμα με χρήση weak:
Αλλάζουμε την αναφορά tenant στην κλάση Apartment σε ασθενή:
class Apartment {
let unit: String
weak var tenant: Person? // Χρησιμοποιούμε weak
init(unit: String) {
self.unit = unit
print("Apartment \(unit) αρχικοποιείται")
}
deinit {
print("Apartment \(unit)) απελευθερώνεται")
}
}
Τώρα, όταν το αντικείμενο Person απελευθερώνεται, η αναφορά tenant στο Apartment γίνεται αυτόματα nil, διακόπτοντας τον κύκλο διατήρησης και επιτρέποντας στο Apartment επίσης να απελευθερωθεί.
Πίνακας σύγκρισης weak και unowned:
| Χαρακτηριστικό | weak |
unowned |
|---|---|---|
| Τύπος αναφοράς | Προαιρετική | Μη προαιρετική |
Μπορεί να είναι nil? |
Ναι, όταν το αντικείμενο απελευθερώνεται | Όχι |
| Πότε να χρησιμοποιείται | Όταν το αντικείμενο μπορεί να γίνει nil |
Όταν το αντικείμενο δεν μπορεί να είναι nil |
| Απόδοση | Ελαφρώς πιο αργό λόγω ελέγχου nil |
Ελαφρώς πιο γρήγορο |
Επίσης, οι κύκλοι διατήρησης μπορούν να προκύψουν και σε κλείσιμο (closures).
Όταν ένα κλείσιμο καταγράφει ιδιότητες της κλάσης ή το self, μπορεί να δημιουργήσει ισχυρή αναφορά στο αντικείμενο. Αν αυτό το αντικείμενο έχει επίσης ισχυρή αναφορά στο κλείσιμο (π.χ. μέσω ιδιότητας-κλεισίματος ή delegate), δημιουργείται κύκλος διατήρησης.
Πώς να λύσετε το πρόβλημα των κύκλων διατήρησης σε κλεισίματα:
Χρησιμοποιήστε λίστα καταγραφής (capture list).
class SomeClass {
var value = 0
func doSomethingDelayed() {
DispatchQueue.main.asyncAfter(deadline: .now() + 1) { [weak self] in // λίστα capture με weak self
guard let self = self else { return } // έλεγχος `nil` για το weak self
self.value += 1
print("Τιμή τώρα \(self.value)")
}
}
}
Σε αυτό το παράδειγμα, το [weak self] δημιουργεί ασθενή αναφορά στο self μέσα στο κλείσιμο, αποτρέποντας τον κύκλο διατήρησης.