Gestion de Memoria
## Objetivos de Aprendizaje
- Comprender conceptos basicos de ARC
- Trabajar con referencias weak y unowned
- Evitar ciclos de retencion
- Dominar patrones de gestion de memoria
## Conteo Automatico de Referencias
### Como Funciona ARC
Swift usa Conteo Automatico de Referencias (ARC) para gestionar memoria:
```swift
class Persona {
var nombre: String
init(nombre: String) {
self.nombre = nombre
}
deinit {
print("\(nombre) esta siendo desasignado")
}
}
var persona1: Persona?
var persona2: Persona?
var persona3: Persona?
persona1 = Persona(nombre: "Alicia") // Conteo de referencias: 1
persona2 = persona1 // Conteo de referencias: 2
persona3 = persona1 // Conteo de referencias: 3
persona1 = nil // Conteo de referencias: 2
persona2 = nil // Conteo de referencias: 1
persona3 = nil // Conteo de referencias: 0 -> DESASIGNADO
```
### Reglas de Conteo de Referencias
| Accion | Efecto |
|--------|--------|
| Asignar a variable | +1 referencia |
| Variable sale del alcance | -1 referencia |
| Asignar nil | -1 referencia |
| Copiar referencia | +1 referencia |
## Referencias Fuertes
### Comportamiento Predeterminado
```swift
class Profesor {
var nombre: String
init(nombre: String) {
self.nombre = nombre
}
deinit {
print("\(nombre) desasignado")
}
}
let profesor = Profesor(nombre: "Sr. Garcia")
// Referencia fuerte por defecto
```
### Ciclo de Vida de Instancia de Clase
```swift
class Cliente {
let nombre: String
init(nombre: String) {
self.nombre = nombre
}
deinit {
print("\(nombre) esta siendo desasignado")
}
}
func crearCliente() {
let cliente = Cliente(nombre: "Alicia")
print("\(cliente.nombre) creado")
}
crearCliente()
// Salida:
// Alicia creado
// Alicia esta siendo desasignado
```
## Referencias Debiles
### Declaracion
```swift
weak var delegate: AlgunDelegate?
```
### Caso de Uso: Delegates
```swift
protocol DelegatePadre: AnyObject {
func hijoActualizado()
}
class Padre {
weak var delegate: DelegatePadre?
func actualizar() {
delegate?.hijoActualizado()
}
}
class Hijo {
var padre: Padre?
init() {
padre = Padre()
padre?.delegate = self
}
deinit {
print("Hijo desasignado")
}
}
extension Hijo: DelegatePadre {
func hijoActualizado() {
print("Actualizado!")
}
}
var hijo: Hijo? = Hijo()
hijo = nil
// Hijo desasignado correctamente - sin ciclo de retencion
```
### Reglas de Referencia Debil
- Siempre opcional (`var`)
- Se establece a `nil` cuando el objeto referenciado se desasigna
- Usar para delegates y referencias padre
## Referencias Unowned
### Declaracion Unowned
```swift
unowned var referencia: AlgunTipo
```
### Caso de Uso: Referencias Capturadas
```swift
class Cliente {
let nombre: String
var tarjeta: TarjetaCredito?
init(nombre: String) {
self.nombre = nombre
}
deinit {
print("Cliente \(nombre) desasignado")
}
}
class TarjetaCredito {
let numero: Int
unowned let propietario: Cliente
init(numero: Int, propietario: Cliente) {
self.numero = numero
self.propietario = propietario
}
deinit {
print("Tarjeta \(numero) desasignada")
}
}
var cliente: Cliente? = Cliente(nombre: "Alicia")
cliente?.tarjeta = TarjetaCredito(numero: 1234, propietario: cliente!)
// El unowned owner de TarjetaCredito no aumenta el conteo de retencion
cliente = nil
// Cliente desasignado, luego TarjetaCredito desasignada
```
### unowned vs weak
| Caracteristica | weak | unowned |
|----------------|------|---------|
| Tipo | Opcional | No opcional |
| Valor cuando se desasigna | nil | Crashea si se accede |
| Usar cuando | Puede ser nil | Nunca nil |
| Uso tipico | Delegates | Padre/hijo |
## Ciclos de Retencion
### Que es un Ciclo de Retencion?
```swift
class Persona {
var nombre: String
var amigo: Persona? // Fuerte por defecto
init(nombre: String) {
self.nombre = nombre
}
deinit {
print("Persona \(nombre) desasignada")
}
}
var alice: Persona?
var bob: Persona?
alice = Persona(nombre: "Alicia")
bob = Persona(nombre: "Bob")
alice?.amigo = bob // Alice -> Bob (fuerte)
bob?.amigo = alice // Bob -> Alice (fuerte)
// Ciclo de retencion: Alice <-> Bob
// Ninguno sera nunca desasignado!
```
### Romper el Ciclo
```swift
class Persona {
var nombre: String
weak var amigo: Persona? // weak rompe el ciclo
init(nombre: String) {
self.nombre = nombre
}
deinit {
print("Persona \(nombre) desasignada")
}
}
```
## Capturas de Closures
### Closures y Referencias
```swift
class ManejadorClosure {
var valor = 10
lazy var closure: () -> Int = {
return self.valor // Captura fuerte de self
}
}
```
### Listas de Captura
```swift
class Manejador {
var valor = 10
lazy var closureDebil: () -> Int = { [weak self] in
return self?.valor ?? 0 // Captura debil
}
lazy var closureUnowned: () -> Int = { [unowned self] in
return self.valor // Captura unowned
}
}
```
### Por Que Listas de Captura?
```swift
class MiControladorVista {
var datos: [String] = []
func cargarDatos() {
// Sin lista de captura - CICLO DE RETENCION!
// completion: () -> Void = { self.datos = [...] }
// Con lista de captura - seguro
API.obtener { [weak self] resultado in
self?.datos = resultado
}
}
}
```
## Patrones de Gestion de Memoria
### Patron Delegate
```swift
protocol DelegadoManejador: AnyObject {
func completo()
}
class Manejador {
weak var delegate: DelegadoManejador?
}
```
### Referencias Padre-Hijo
```swift
class Padre {
var hijos: [Hijo] = []
func agregarHijo(_ hijo: Hijo) {
hijos.append(hijo)
hijo.padre = self // Fuerte
}
}
class Hijo {
weak var padre: Padre? // Debil - previene ciclo
}
```
### Patron Fuente de Datos
```swift
class CeldaTablaVista {
weak var fuenteDatos: FuenteDatosTablaVista?
}
```
## Depurar Problemas de Memoria
### Depurador de Grafos de Memoria (Xcode)
1. Ejecutar app en modo debug
2. Debug > Depurar Grafos de Memoria
3. Buscar ciclos y fugas
### Instruments
- Instrumento Allocations
- Instrumento Leaks
- Instrumento Core Allocation
### Sintomas Comunes
- Memoria sigue creciendo
- deinit nunca llamado
- App usando mas RAM de lo esperado
## Mejores Practicas
### Hacer
```swift
// Usar weak para delegates
weak var delegate: MiDelegate?
// Usar weak/weak self en closures
algunLlamadoAsincrono { [weak self] resultado in
self?.manejar(resultado)
}
// Usar unowned cuando es seguro
lazy var closure: () -> String = { [unowned self] in
return self.id
}
```
### No Hacer
```swift
// No crear ciclos de referencia fuertes
class A {
var b: B?
}
class B {
var a: A? // Ciclo si ambos se referencian fuertemente
}
// No usar unowned cuando es opcional
// MAL: unowned var referenciaOpcional: String? (error de compilador)
```
## Tipos Valor vs Tipos Referencia
### Tipos Valor (Struct, Enum)
```swift
struct Punto {
var x: Int
var y: Int
}
var p1 = Punto(x: 1, y: 2)
var p2 = p1 // Copia! Sin conteo de referencias
p2.x = 10
print(p1.x) // 1 (sin cambios)
print(p2.x) // 10
```
### Tipos Referencia (Class)
```swift
class PuntoClase {
var x: Int
var y: Int
init(x: Int, y: Int) {
self.x = x
self.y = y
}
}
var p1 = PuntoClase(x: 1, y: 2)
var p2 = p1 // Misma referencia!
p2.x = 10
print(p1.x) // 10 (cambiado!)
print(p2.x) // 10
```
## Resumen
- ARC gestiona memoria automaticamente para instancias de clase
- Referencias fuertes aumentan el conteo de retencion
- Referencias debiles (`weak var`) no aumentan el conteo, pueden ser nil
- Referencias unowned (`unowned var`) no aumentan el conteo, nunca nil
- Ciclos de retencion ocurren con referencias circulares fuertes
- Usar weak para delegates y referencias padre
- Usar listas de captura en closures: `[weak self]` o `[unowned self]`
- Tipos valor (structs) son copiados, no contados por referencia
- Tipos referencia (classes) comparten referencias
Comments
Comments powered by Giscus
To enable comments, add your Giscus embed code here.
Learn more about Giscus →