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
nilal 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
weakcuando la referencia puede volversenil(el objeto puede ser liberado primero). - Usa
unownedcuando 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.