Πρέπει πάντα να χρησιμοποιείτε weak ή unowned στη λίστα σύλληψης;
sobes.tech AI
Απάντηση από AI
Όχι, όχι πάντα.
Χρησιμοποιούμε weak ή unowned στη λίστα Capture για την αποτροπή κύκλων διατήρησης.
Ο κύκλος διατήρησης προκύπτει όταν δύο ή περισσότεροι αντικείμενα αναφέρονται ισχυρά το ένα στο άλλο, εμποδίζοντας την απελευθέρωση της μνήμης.
Το weak ή unowned πρέπει να χρησιμοποιούνται όταν μια κλείδωση αναφέρεται ισχυρά στο self (ή σε άλλη μεταβλητή στιγμιότυπου της κλάσης/ηθοποιού), και με τη σειρά της το self (ή άλλο αντικείμενο) έχει ισχυρή αναφορά σε αυτήν την κλείδωση.
Παραδείγματα καταστάσεων όπου αυτό είναι απαραίτητο:
- Ασύγχρονες λειτουργίες (αιτήματα δικτύου, επεξεργασία δεδομένων)
- Πρότυπο Observer
- Μπλοκς κινούμενων σχεδίων
- Ουρές διανομής
Εάν η κλείδωση δεν καταλαμβάνει το self (ή άλλη μεταβλητή κλάσης/ηθοποιού που έχει ισχυρή αναφορά στην κλείδωση), ή καταλαμβάνει δομικούς τύπους/καταλόγους, η χρήση weak ή unowned δεν είναι απαραίτητη.
Διαφορά μεταξύ weak και unowned:
| Τύπος | Περιγραφή | Πότε να χρησιμοποιήσετε |
|---|---|---|
weak |
Αδύναμη (προαιρετική) αναφορά. Μπορεί να γίνει nil. |
Όταν ο κύκλος ζωής της κλείδωσης είναι μικρότερος ή ίσος με αυτόν του καταληφθέντος αντικειμένου, και το αντικείμενο μπορεί να γίνει nil πριν από την ολοκλήρωση της κλείδωσης. |
unowned |
Μη-κύρια (μη-προαιρετική) αναφορά. Δεν μπορεί να γίνει nil. |
Όταν ο κύκλος ζωής της κλείδωσης είναι μικρότερος ή ίσος με αυτόν του καταληφθέντος αντικειμένου, και εγγυάται ότι το αντικείμενο θα υπάρχει μέχρι την ολοκλήρωση της κλείδωσης. Αν το αντικείμενο απελευθερωθεί πριν από την εκτέλεση της κλείδωσης, θα προκληθεί σφάλμα (αναφορά unowned σε αποδεσμευμένη περίπτωση). |
Παράδειγμα χρήσης weak στη λίστα Capture:
class MyClass {
var name: String = "Test"
func performAsyncOperation() {
DispatchQueue.global().async { [weak self] in
guard let self = self else {
return
}
print("Ολοκλήρωση λειτουργίας για \(self.name)")
}
}
deinit {
print("MyClass απελευθερώθηκε")
}
}
Παράδειγμα χρήσης unowned στη λίστα Capture:
class ViewController {
var dismissButton: UIButton!
func setupButton() {
dismissButton.addTarget(self, action: #selector(dismiss), for: .touchUpInside)
// Αυτό το παράδειγμα δείχνει ότι το `self` (ViewController) ζει περισσότερο ή ίσο με τη δράση του κουμπιού.
// Η target-action δεν είναι κλείδωση, αλλά δείχνει την έννοια του unowned σε περίπτωση ισχυρού κύκλου διατήρησης.
// Αν ήταν κλείδωση, π.χ., στον χειριστή UIControl.Event,
// και το ViewController υπάρχει σίγουρα κατά τη διάρκεια του πατήματος:
// dismissButton.addAction(UIAction { [unowned self] _ in
// self.dismiss(animated: true, completion: nil)
// }, for: .touchUpInside)
}
@objc func dismiss() {
// Απόρριψη view controller
}
}
Εάν η κλείδωση δεν δημιουργεί κύκλο διατήρησης, η χρήση weak ή unowned δεν είναι απαραίτητη και μπορεί ακόμη και να οδηγήσει σε περιττούς ελέγχους (στην περίπτωση του weak) ή σε πιθανά σφάλματα (στην περίπτωση του unowned).
Για παράδειγμα, κλείδωση που δεν καταλαμβάνει self ή άλλο αντικείμενο, το οποίο έχει ισχυρή αναφορά στην κλείδωση:
func fetchData(completion: @escaping (Data) -> Void) {
URLSession.shared.dataTask(with: URL(string: "...")!) { data, response, error in
guard let data = data else { return }
// Εδώ δεν υπάρχει καταγραφή του self ή άλλων αντικειμένων που μπορούν να προκαλέσουν κύκλο διατήρησης.
completion(data)
}.resume()
}
Σε αυτή την περίπτωση, [weak self] ή [unowned self] στη λίστα Capture δεν είναι απαραίτητα.