Weak Reference — qué es, sintaxis y uso en desarrollo móvil

Autor: IT Sectr Publicado: 2026-03-30 Tiempo de lectura: 9 min

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 Reference — referencia que no afecta el retain count del objeto; se anula al liberarse el objeto
  • Declaración — palabra clave weak antes de var; tipo siempre opcional (?)
  • Uso — delegados, clausuras, relaciones padre-hijo para romper ciclos de retención
  • Seguridad — establecimiento automático a nil después de la desasignación del objeto (zeroing weak)
  • Diferencia con unowned — weak se anula y es seguro, unowned no se anula y requiere garantías de tiempo de vida

¿Qué es Weak Reference?

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.

Sintaxis de weak en Swift y Objective-C

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.

Swift

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.

swift
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.

Objective-C

En Objective-C, las propiedades débiles se declaran usando el atributo __weak o el modificador weak en declaraciones de propiedad:

objective-c
// 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.

Cuándo usar referencias débiles

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 (patrón Delegate)

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.

Padre-Hijo con referencia inversa

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.

Clausuras asíncronas

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.

EscenarioWeakStrong
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.

Weak vs Unowned: comparación y escenarios

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.

Diferencias

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.

Cuándo elegir weak

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.

Cuándo elegir unowned

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.

swift
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.

Limitaciones de las referencias débiles y trampas

Las referencias débiles son una herramienta poderosa, pero tienen limitaciones que es importante entender para un uso correcto en el desarrollo de iOS.

Rendimiento de weak

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.

Weak no es aplicable a tipos valor

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.

Weak en múltiples hilos

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.

swift
// 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.

UIView y weak outlets

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

¿Puede una referencia débil apuntar a un objeto que aún no se ha creado?

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.

¿Por qué weak solo funciona con tipos class?

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.

¿Cómo afecta weak al rendimiento en un bucle?

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.

¿Cuándo puede una referencia débil volverse nil inesperadamente?

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.

¿En qué se diferencia weak de __weak en Objective-C?

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 Reference — referencia no propietaria que no aumenta el retain count y se anula automáticamente al liberarse
  • Sintaxisweak var + tipo opcional; solo tipos class y protocolos AnyObject
  • Zeroing weak — el runtime anula todas las referencias débiles a un objeto liberado, evitando punteros colgantes
  • Escenarios — delegados, padre-hijo con referencia inversa, clausuras asíncronas ([weak self])
  • Weak vs Unowned — weak se anula (seguro), unowned no se anula (riesgo de crash, pero no opcional)
  • Rendimiento — weak es más lento que strong debido al lookup en la tabla del runtime; para rutas críticas, copia a strong
  • Recomendación — si no estás seguro de las garantías de tiempo de vida, elige weak

Desarrollaremos 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.

Discutir el proyecto

Lea también