¿Cómo funciona el mecanismo de gestión automática de memoria (ARC) en el lenguaje Swift?
sobes.tech AI
Respuesta de la IA
ARC (Contador de Referencias Automático) rastrea y gestiona automáticamente las referencias a instancias de clases. Libera la memoria ocupada por una instancia cuando ya no hay referencias fuertes a ella. ARC solo funciona con instancias de clases, no con estructuras o enumeraciones, ya que son tipos de valor.
Tres tipos de enlaces entre instancias:
- Strong (Fuerte): Un enlace que incrementa el contador de referencias de la instancia. Por defecto, todas las referencias son fuertes.
- Weak (Débil): Un enlace que no incrementa el contador de referencias. Se usa para prevenir ciclos de referencias fuertes. Las referencias de tipo
weaksiempre son opcionales, ya que la instancia puede ser liberada en cualquier momento. - Unowned (Sin dueño): Un enlace que no incrementa el contador de referencias y se usa cuando se garantiza que la instancia relacionada existirá más tiempo que la actual. No es obligatorio, pero intentar acceder a una instancia liberada provocará un error en tiempo de ejecución.
Los ciclos de referencias fuertes ocurren cuando dos o más instancias de clases tienen referencias fuertes entre sí, impidiendo su liberación por ARC. Para resolver estos ciclos, se usan referencias weak o unowned.
Ejemplo de ciclo de referencias fuertes:
class Person {
let name: String
var apartment: Apartment?
init(name: String) {
self.name = name
}
deinit { // llamado al liberar
print("\(name) está siendo desalojado")
}
}
class Apartment {
let unit: String
var tenant: Person?
init(unit: String) {
self.unit = unit
}
deinit { // llamado al liberar
print("Apartamento \(unit) está siendo desalojado")
}
}
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 // No se imprime nada, ya que hay un ciclo de referencias fuertes
Resolución del ciclo con weak:
class Person {
let name: String
var apartment: Apartment?
init(name: String) {
self.name = name
}
deinit {
print("\(name) está siendo desalojado")
}
}
class Apartment {
let unit: String
weak var tenant: Person? // Referencia débil a Person
init(unit: String) {
self.unit = unit
}
deinit {
print("Apartamento \(unit) está siendo desalojado")
}
}
var john: Person?
var unit4A: Apartment?
john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")
john!.apartment = unit4A
unit4A!.tenant = john
john = nil // Libera a Person
unit4A = nil // Libera a Apartment
Resolución con unowned:
class Customer {
let name: String
var card: CreditCard?
init(name: String) {
self.name = name
}
deinit {
print("\(name) está siendo desalojado")
}
}
class CreditCard {
let number: UInt64
unowned let customer: Customer // Referencia sin dueño a Customer
init(number: UInt64, customer: Customer) {
self.number = number
self.customer = customer
}
deinit {
print("Tarjeta #\(number) está siendo desalojada")
}
}
var john: Customer?
john = Customer(name: "John Appleseed")
john!.card = CreditCard(number: 1234_5678_9012_3456, customer: john!)
john = nil // Libera a Customer y CreditCard
ARC en cierres: Los cierres también pueden crear ciclos de referencias fuertes, capturando instancias de clases. Para prevenir esto, se usan listas de captura ([weak self] o [unowned self]).
class HTMLElement {
let name: String
let text: String?
lazy var asHTML: () -> String = { // lazy para poder usar self antes de la inicialización
[unowned self] in // Lista de captura con "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) está siendo desalojado")
}
}
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
paragraph = nil // Libera HTMLElement