Πώς λειτουργεί ο μηχανισμός αυτόματης διαχείρισης μνήμης (ARC) στη γλώσσα Swift;
sobes.tech AI
Απάντηση από AI
Το ARC (Αυτόματη Αριθμητική Αναφορά) παρακολουθεί και διαχειρίζεται αυτόματα τις αναφορές σε στιγμιότυπα κλάσεων. Απελευθερώνει τη μνήμη που καταλαμβάνει ένα στιγμιότυπο όταν δεν υπάρχουν πλέον ισχυρές αναφορές σε αυτό. Το ARC λειτουργεί μόνο με στιγμιότυπα κλάσεων, όχι με δομές ή καταλόγους, καθώς αυτοί είναι τύποι-τιμές.
Τρεις τύποι αναφορών μεταξύ στιγμιότυπων:
- Strong (Ισχυρή): Μια αναφορά που αυξάνει τον μετρητή αναφορών ενός στιγμιότυπου. Από προεπιλογή, όλες οι αναφορές είναι ισχυρές.
- Weak (Αδύναμη): Μια αναφορά που δεν αυξάνει τον μετρητή αναφορών. Χρησιμοποιείται για την αποτροπή κυκλικών αναφορών ισχυρών. Οι αναφορές
weakείναι πάντα προαιρετικές, καθώς το στιγμιότυπο μπορεί να απελευθερωθεί οποιαδήποτε στιγμή. - Unowned (Ανεξάρτητη): Μια αναφορά που δεν αυξάνει τον μετρητή αναφορών και χρησιμοποιείται όταν είναι εγγυημένο ότι το συσχετισμένο στιγμιότυπο θα υπάρχει περισσότερο από το τρέχον. Δεν είναι υποχρεωτικό, αλλά η πρόσβαση σε απελευθερωμένο στιγμιότυπο προκαλεί σφάλμα εκτέλεσης.
Οι κυκλικές ισχυρές αναφορές εμφανίζονται όταν δύο ή περισσότερα στιγμιότυπα κλάσεων έχουν ισχυρές αναφορές το ένα στο άλλο, εμποδίζοντας την απελευθέρωσή τους από το ARC. Για την επίλυση αυτών των κυκλικών αναφορών, χρησιμοποιούνται weak ή unowned αναφορές.
Παράδειγμα κυκλικής ισχυρής αναφοράς:
class Person {
let name: String
var apartment: Apartment?
init(name: String) {
self.name = name
}
deinit { // καλείται κατά την απελευθέρωση
print("\(name) απελευθερώνεται")
}
}
class Apartment {
let unit: String
var tenant: Person?
init(unit: String) {
self.unit = unit
}
deinit { // καλείται κατά την απελευθέρωση
print("Διαμέρισμα \(unit) απελευθερώνεται")
}
}
var john: Person?
var unit4A: Apartment?
john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")
john!.apartment = unit4A
unit4A!.tenant = john
john = nil
unit4A = nil // Τίποτα δεν εμφανίζεται, επειδή υπάρχει κυκλική ισχυρή αναφορά
Επίλυση κυκλικής ισχυρής αναφοράς με weak:
class Person {
let name: String
var apartment: Apartment?
init(name: String) {
self.name = name
}
deinit {
print("\(name) απελευθερώνεται")
}
}
class Apartment {
let unit: String
weak var tenant: Person? // Αδύναμη αναφορά
init(unit: String) {
self.unit = unit
}
deinit {
print("Διαμέρισμα \(unit) απελευθερώνεται")
}
}
var john: Person?
var unit4A: Apartment?
john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")
john!.apartment = unit4A
unit4A!.tenant = john
john = nil // Απελευθερώνει το Person
unit4A = nil // Απελευθερώνει το Διαμέρισμα
Επίλυση κυκλικής ισχυρής αναφοράς με unowned:
class Customer {
let name: String
var card: CreditCard?
init(name: String) {
self.name = name
}
deinit {
print("\(name) απελευθερώνεται")
}
}
class CreditCard {
let number: UInt64
unowned let customer: Customer // Ανεξάρτητη αναφορά
init(number: UInt64, customer: Customer) {
self.number = number
self.customer = customer
}
deinit {
print("Κάρτα #\(number) απελευθερώνεται")
}
}
var john: Customer?
john = Customer(name: "John Appleseed")
john!.card = CreditCard(number: 1234_5678_9012_3456, customer: john!)
john = nil // Απελευθερώνει το Customer και την CreditCard
ARC σε closures: Τα closures μπορούν επίσης να δημιουργήσουν κυκλικές αναφορές, κρατώντας στιγμιότυπα κλάσεων. Για την αποφυγή, χρησιμοποιούνται λίστες καταγραφής ([weak self] ή [unowned self]).
class HTMLElement {
let name: String
let text: String?
lazy var asHTML: () -> String = { // lazy για χρήση του self πριν την αρχικοποίηση
[unowned self] in // Λίστα καταγραφής με "unowned self"
if let text = self.text {
return "<\(self.name)>\(text)</\(self.name)>"
} else {
return "<\(self.name) />"
}
}
init(name: String, text: String? = nil) {
self.name = name
self.text = text
}
deinit {
print("\(name) απελευθερώνεται")
}
}
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "γεια σου, κόσμε")
print(paragraph!.asHTML())
paragraph = nil // HTMLElement απελευθερώνεται