Sobes.tech
Middle

Πώς λειτουργεί ο μηχανισμός αυτόματης διαχείρισης μνήμης (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 απελευθερώνεται