Weak Reference (referencia débil) es una referencia a un objeto que no aumenta su contador de retención en ARC. Según Apple Swift Language Guide, 2026, las referencias débiles se declaran con la palabra clave weak y siempre son opcionales. Cuando el objeto se libera, todas las referencias débiles a él se establecen automáticamente en nil, evitando punteros colgantes y convirtiendo las referencias débiles en un mecanismo seguro para romper ciclos de retención.
Puntos clave
weak antes de var; tipo siempre opcional (?)Weak Reference es una referencia no propietaria a un objeto en ARC (Automatic Reference Counting). A diferencia de una referencia fuerte, que aumenta el retain count del objeto y garantiza su vida útil, una referencia débil permite que el objeto se libere incluso si todavía se hace referencia a él. Después de la liberación, la referencia débil se establece automáticamente en nil — esto se llama zeroing weak.
Zeroing weak es una característica clave del runtime de Swift y Objective-C. Cuando el contador de referencias de un objeto llega a cero y el objeto se desasigna, el runtime recorre todas las referencias débiles a este objeto (almacenadas en una tabla débil especial) y las establece en nil. Esto garantiza que el acceso a memoria liberada (use-after-free) sea imposible a través de referencias débiles — cualquier lectura devuelve nil.
Según Apple WWDC 2012 Session 406, las referencias débiles zeroing eliminaron toda una clase de errores de crash relacionados con punteros colgantes (dangling pointers), que eran comunes en la gestión manual de memoria (MRR). En MRR, las referencias débiles solo existían como __unsafe_unretained — no se anulaban, y acceder a un objeto liberado provocaba EXC_BAD_ACCESS.
Veamos la sintaxis para declarar referencias débiles en ambos lenguajes del ecosistema Apple. A pesar del runtime compartido, la sintaxis difiere, pero la semántica es idéntica.
En Swift, las referencias débiles se declaran con la palabra clave weak antes de var. El tipo siempre debe ser opcional (Type?), ya que la referencia puede anularse en cualquier momento. Las constantes (let) no pueden ser weak — solo las variables.
class ViewController: UIViewController {
// weak properties: only var, only optional
weak var delegate: ViewControllerDelegate?
weak var parentView: UIView?
weak var completionHandler: ((Bool) -> Void)? // ⚠️ las clausuras no almacenan weak
// ⬆️ Error: weak solo se puede aplicar a tipos class, no a closures
}
Importante: weak solo es aplicable a instancias de clase (tipos class), AnyObject y protocolos heredados de AnyObject. Struct, enum y clausuras no pueden ser weak — son tipos valor y no participan en ARC.
En Objective-C, las propiedades débiles se declaran usando el atributo __weak o el modificador weak en declaraciones de propiedad:
// Objective-C: weak property
@interface MyViewController : UIViewController
@property (weak, nonatomic) id<MyDelegate> delegate;
@end
// Variable local débil
__weak MyObject *weakRef = someStrongObject;
El runtime de Objective-C también proporciona zeroing weak, pero adicionalmente bloquea el uso de weak con estructuras C y algunos objetos Core Foundation. Para estos, se usa __unsafe_unretained — sin zeroing.
Las referencias débiles no son una solución universal, sino una herramienta para escenarios específicos. Usar weak en todas partes lleva a una complejidad innecesaria y perjudica la legibilidad. Veamos los escenarios de uso correctos.
Delegados — el escenario principal para weak. El objeto propietario (por ejemplo, UITableView) mantiene una referencia fuerte a sí mismo, mientras que el delegado (UIViewController) no debe ser propietario de la tabla. Apple SDK garantiza que todos los delegates y dataSources son weak. Para tus propios protocolos, usa siempre weak var delegate.
Cuando un objeto hijo necesita referenciar a su padre (por ejemplo, ChildViewController accediendo a un coordinador), usa una referencia débil. El padre es propietario del hijo (strong), el hijo observa al padre (weak) — el ciclo de retención está eliminado.
Capture list [weak self] — la forma estándar de evitar ciclos de retención en clausuras almacenadas como propiedades de clase. Si self puede liberarse antes de que la clausura termine, weak self es obligatorio.
| Escenario | Weak | Strong |
|---|---|---|
| Delegate | ✅ Siempre weak | ❌ Retain cycle |
| Padre → Hijo | ❌ No necesario (padre debe poseer) | ✅ Strong |
| Hijo → Padre | ✅ Weak | ❌ Retain cycle |
| Callback asíncrono | ✅ [weak self] | ❌ Riesgo de retain cycle |
| Acoplamiento fuerte (owned) | ❌ unowned | ✅ Strong |
Regla general: si el objeto A posee a B (A → B strong), entonces B → A debe ser weak o unowned. La dirección de las referencias fuertes debe ser siempre del propietario al subordinado.
Tanto weak como unowned no aumentan el retain count, pero difieren en el comportamiento después de la desasignación del objeto. La elección entre ellos es cuestión de garantías de tiempo de vida.
Weak: se anula automáticamente (nil), el tipo siempre es opcional, requiere unwrap antes de usar. Seguro — acceder a nil no provoca crash.
Unowned: no se anula, el tipo es no opcional. Si el objeto se libera, una referencia unowned se convierte en un puntero colgante — acceder a ella provoca un crash en tiempo de ejecución. Unowned asume que el objeto vive al menos tanto como la parte que lo referencia.
Elige weak si: el objeto puede liberarse en cualquier momento (delegado después de cerrar la pantalla), no controlas el tiempo de vida del objeto, o dudas sobre las garantías. Weak es la opción segura universal.
Elige unowned si: el objeto está garantizado a no liberarse antes que el objeto que lo referencia (por ejemplo, Cliente → TarjetaCredito, donde la tarjeta no existe sin el cliente). Unowned proporciona una API no opcional sin unwrap, lo que es más conveniente en el código.
class Order {
let id: Int
var items: [Item] = []
init(id: Int) { self.id = id }
// Relación fuerte: Order es propietario de Item
func addItem(name: String) {
let item = Item(name: name, order: self)
items.append(item)
}
}
class Item {
let name: String
unowned let order: Order // ✅ unowned — Item no vive sin Order
init(name: String, order: Order) {
self.name = name
self.order = order
}
}
// Ejemplo con weak: delegado sin garantía de tiempo de vida
protocol NetworkServiceDelegate: AnyObject {
func didReceiveResponse(data: Data)
}
class NetworkService {
weak var delegate: NetworkServiceDelegate? // ✅ weak — el delegado puede desaparecer
}
En el ejemplo, Item usa unowned porque un elemento de pedido no puede existir sin el propio pedido — la garantía de tiempo de vida es sólida. NetworkService usa weak porque el delegado (por ejemplo, ViewController) puede cerrarse y liberarse en cualquier momento.
Las referencias débiles son una herramienta poderosa, pero tienen limitaciones que es importante entender para un uso correcto en el desarrollo de iOS.
Las referencias débiles son más lentas que las fuertes: en cada acceso, el runtime verifica si el objeto ha sido liberado (lookup en la tabla débil). En la gran mayoría de escenarios, la diferencia es imperceptible, pero en bucles críticos con millones de accesos, weak puede convertirse en un cuello de botella. Para escenarios de alta carga, usa strong y reorganiza la arquitectura.
Struct, enum, tuple — tipos valor que no participan en ARC. Intentar declarar un weak struct provoca un error de compilación. Para almacenar una referencia débil a un tipo valor, usa un envoltorio en un tipo clase o una clausura.
Zeroing weak es seguro para hilos: si un objeto se libera en un hilo, la referencia débil se anula en todos los hilos atómicamente. Sin embargo, la ventana entre leer una referencia débil y desreferenciarla puede provocar una condición de carrera — el objeto se libera entre la obtención de la referencia débil y su uso. Solución: captura fuerte de la referencia débil en una variable local.
// Race condition con weak en múltiples hilos
func performAsync() {
weak var weakSelf = self
queue.async {
// ⚠️ weakSelf puede ser nil entre la comprobación y el uso
if weakSelf != nil {
weakSelf!.doSomething() // CRASH si se vuelve nil
}
}
}
// ✅ Corrección: captura fuerte durante el uso
func performAsyncSafe() {
queue.async { [weak self] in
guard let strongSelf = self else { return }
strongSelf.doSomething() // strongSelf — referencia local fuerte
}
}
En la versión segura, weak self se captura y luego se desempaqueta inmediatamente en una variable local fuerte strongSelf. Si self sigue vivo, permanecerá vivo durante la ejecución del bloque. Si no, guard se activa y el código no se ejecuta. Este idiom es el patrón estándar para clausuras asíncronas en Swift.
IBOutlet en Interface Builder deben ser weak porque la jerarquía de vistas ya mantiene una referencia fuerte a la subvista. Duplicar una referencia fuerte en el controlador no crea un ciclo de retención pero es redundante. Una referencia débil a un outlet es la recomendación de Apple, aunque muchos desarrolladores usan strong para simplificar el código.
Preguntas frecuentes
No, weak solo puede apuntar a un objeto existente o nil. Al crear un nuevo objeto, primero obtienes una referencia fuerte (a través de un inicializador), y solo entonces puedes asignar una referencia débil. Un weak nil al inicio es un estado normal.
Weak se basa en ARC, que solo gestiona tipos referencia (clases). Los tipos valor (struct, enum) se copian al asignarse y no tienen retain count. Para relaciones débiles con tipos valor, usa clausuras o envoltorios en una clase con una propiedad weak.
Cada acceso a una referencia débil realiza un lookup en la tabla del runtime. En un bucle con millones de iteraciones, esto puede ser de 2 a 5 veces más lento que una referencia fuerte. Para rutas críticas, copia weak en una variable local fuerte antes del bucle.
Cuando todas las referencias fuertes al objeto se pierden — al final del ámbito, al reasignar una propiedad, o al cerrar una pantalla. En un entorno multiproceso, esto puede ocurrir entre dos líneas de código. Siempre verifica las referencias débiles con guard let o if let.
Semánticamente idénticos: ambos proporcionan zeroing weak. Diferencias: Swift requiere un tipo opcional y var, Objective-C usa un modificador de propiedad. Objective-C también admite __unsafe_unretained — una referencia débil sin zeroing (riesgo de puntero colgante).
Resumen
weak var + tipo opcional; solo tipos class y protocolos AnyObjectDesarrollaremos una aplicación móvil llave en mano
IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.
Lea también