Sobes.tech
Middle

Cuéntenos sobre la gestión de memoria en iOS.

sobes.tech AI

Respuesta de la IA

iOS utiliza recuento automático de referencias (ARC) para gestionar la memoria. ARC libera automáticamente los objetos tan pronto como dejan de ser referenciados.

Conceptos principales:

  • Referencia fuerte: Incrementa el contador de referencias del objeto. El objeto no será liberado mientras existan referencias fuertes.
  • Referencia débil: No incrementa el contador de referencias. Se usa para prevenir ciclos de referencias fuertes. Hace referencia a un objeto que puede ser liberado. Si el objeto se libera, la referencia débil se vuelve nil.
  • Referencia no poseída (unowned): Similar a débil, no incrementa el contador de referencias. Se usa cuando el objeto referenciado tiene un ciclo de vida igual o más largo. La referencia no poseída no se vuelve nil al liberar el objeto. Intentar acceder a un objeto liberado a través de una referencia no poseída genera un error en tiempo de ejecución.

Ciclos de referencias fuertes (Retain Cycles): Ocurren cuando dos o más objetos se refieren entre sí con referencias fuertes, impidiendo que sean liberados. Se resuelven usando referencias débil o no poseídas.

Ejemplo de ciclo de referencias fuertes y su solución:

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

    init(name: String) {
        self.name = name
        print("\(name) está siendo inicializado")
    }

    deinit {
        print("\(name) está siendo desinicializado")
    }
}

class Apartment {
    let unit: String
    var tenant: Person? // Referencia fuerte que causa ciclo de retención

    init(unit: String) {
        self.unit = unit
        print("Apartamento \(unit) está siendo inicializado")
    }

    deinit {
        print("Apartamento \(unit) está siendo desinicializado")
    }
}

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

john!.apartment = unit4A
unit4A!.tenant = john // Aquí ocurre un ciclo de retención

john = nil
unit4A = nil
// Los métodos de desinicialización no se llaman debido al ciclo de retención

Solución usando weak:

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

    init(name: String) {
        self.name = name
        print("\(name) está siendo inicializado")
    }

    deinit {
        print("\(name) está siendo desinicializado")
    }
}

class Apartment {
    let unit: String
    weak var tenant: Person? // Uso de referencia débil para evitar ciclo de retención

    init(unit: String) {
        self.unit = unit
        print("Apartamento \(unit) está siendo inicializado")
    }

    deinit {
        print("Apartamento \(unit) está siendo desinicializado")
    }
}

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

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

john = nil
unit4A = nil
// Ambos objetos son desinicializados correctamente

No poseído (unowned) vs Débil (weak):

  • Usa weak cuando la referencia puede volverse nil (el objeto puede ser liberado primero).
  • Usa unowned cuando la referencia siempre tendrá un valor (el objeto referenciado tiene un ciclo de vida igual o más largo).

Closures y ciclos de referencias:

Las closures pueden capturar referencias a objetos, creando ciclos de referencias fuertes. Esto se resuelve con una lista de captura (capture list).

Ejemplo:

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

    lazy var asHTML: () -> String = {
        [weak self] in // Captura débil de self
        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) está siendo desinicializado")
    }
}

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

paragraph = nil
// El objeto se desinicializa

Depuración de problemas de memoria:

  • Memory Graph Debugger: En Xcode permite visualizar las conexiones entre objetos y detectar ciclos de referencias fuertes.
  • Instruments (Allocations, Leaks): Herramientas para monitorear la asignación y liberación de memoria, y detectar fugas.