Sobes.tech
Middle

Vorbește-ne despre gestionarea memoriei în iOS.

sobes.tech AI

Răspuns de la AI

iOS utilizează referința automată (ARC) pentru gestionarea memoriei. ARC eliberează automat obiectele imediat ce nu mai există referințe către ele.

Concepte principale:

  • Referință puternică: Crește contorul de referințe al unui obiect. Obiectul nu va fi eliberat atâta timp cât există referințe puternice către el.
  • Referință slabă: Nu crește contorul de referințe. Este folosită pentru a preveni ciclurile de referințe puternice. Se referă la un obiect care poate fi eliberat. Dacă obiectul este eliberat, referința slabă devine nil.
  • Referință neînțeleasă (unowned): Similar cu slabă, nu crește contorul de referințe. Este folosită atunci când obiectul la care se face referire are aceeași sau o durată de viață mai lungă. Referința neînțeleasă nu devine nil la eliberarea obiectului. Accesarea unui obiect eliberat prin referința neînțeleasă duce la o eroare de execuție.

Cicluri de referințe puternice (Retain Cycles): Apare atunci când două sau mai multe obiecte au referințe puternice unul către celălalt, împiedicând eliberarea lor. Se rezolvă cu referințe slabă sau neînțeleasă.

Exemplu de ciclu de referințe puternice și soluția sa:

class Person {
    let name: String
    var apartment: Apartment?

    init(name: String) {
        self.name = name
        print("\(name) este inițializat")
    }

    deinit {
        print("\(name) este dezinitializat")
    }
}

class Apartment {
    let unit: String
    weak var tenant: Person? // Folosește referința slabă pentru a preveni ciclul de referințe

    init(unit: String) {
        self.unit = unit
        print("Apartament \(unit) este inițializat")
    }

    deinit {
        print("Apartament \(unit) este dezinitializat")
    }
}

var john: Person? = Person(name: "John Appleseed")
var unit4A: Apartment? = Apartment(unit: "4A")

john!.apartment = unit4A
unit4A!.tenant = john // Aici apare ciclul de referințe

john = nil
unit4A = nil
// Metodele de dezinitializare nu sunt apelate din cauza ciclului de referințe

Soluție cu weak:

class Person {
    let name: String
    var apartment: Apartment?

    init(name: String) {
        self.name = name
        print("\(name) este inițializat")
    }

    deinit {
        print("\(name) este dezinitializat")
    }
}

class Apartment {
    let unit: String
    weak var tenant: Person? // Folosește referința slabă pentru a preveni ciclul

    init(unit: String) {
        self.unit = unit
        print("Apartament \(unit) este inițializat")
    }

    deinit {
        print("Apartament \(unit) este dezinitializat")
    }
}

var john: Person? = Person(name: "John Appleseed")
var unit4A: Apartment? = Apartment(unit: "4A")

john!.apartment = unit4A
unit4A!.tenant = john

john = nil
unit4A = nil
// Ambele obiecte sunt dezinitializate corect

Diferența între unowned și weak:

  • Folosește weak când referința poate deveni nil (obiectul poate fi eliberat primul).
  • Folosește unowned când referința va avea întotdeauna o valoare (obiectul la care se face referire are aceeași sau o durată de viață mai lungă).

Closures și cicluri de referințe:

Closures pot captura referințe la obiecte, creând cicluri de referințe puternice. Se rezolvă cu o listă de captură (capture list).

Exemplu:

class HTMLElement {
    let name: String
    let text: String?

    lazy var asHTML: () -> String = {
        [weak self] in // Capturează `self` slab
        guard let self = self else {
            return ""
        }
        var html = "<\(self.name)"
        if let text = self.text {
            html += ">\(text)</\(self.name)>"
        } else {
            html += " />"
        }
        return html
    }

    init(name: String, text: String? = nil) {
        self.name = name
        self.text = text
    }

    deinit {
        print("\(name) este dezinitializat")
    }
}

var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())

paragraph = nil
// Obiectul este dezinitializat

Debugging memorie:

  • Memory Graph Debugger: În Xcode, vizualizează conexiunile dintre obiecte și identifică ciclurile de referințe puternice.
  • Instruments (Allocations, Leaks): Instrumente pentru monitorizarea alocării și eliberării memoriei, și detectarea scurgerilor.