ARC: qué es, cómo funciona Automatic Reference Counting en iOS

Autor: IT Sectr Publicado: 2026-03-29 Tiempo de lectura: 8 min

Automatic Reference Counting (ARC) es un sistema de gestión de memoria en Swift y Objective-C que cuenta automáticamente el número de referencias a cada objeto y lo libera cuando el contador llega a cero. Según la Documentación de Apple Swift, 2026, ARC está integrado en el compilador y funciona en tiempo de compilación, insertando llamadas retain/release en los lugares adecuados. A diferencia de Garbage Collection, ARC no requiere un hilo de recolección separado y no crea pausas durante la ejecución de la aplicación.

Puntos clave

  • ARC — Automatic Reference Counting, sistema de gestión de memoria basado en compilador en Swift y Objective-C
  • Cómo funciona — cada objeto tiene un contador de referencias (retain count); al llegar a cero, el objeto se libera inmediatamente
  • Calificadores — strong, weak y unowned determinan cómo afecta una referencia al contador y al ciclo de vida del objeto
  • Diferencia de GC — ARC funciona deterministicamente en tiempo de compilación, sin pausas Stop-The-World ni hilo de recolección en segundo plano
  • Retain Cycle — el principal problema de ARC: si dos objetos se referencian mutuamente mediante strong, su contador nunca llega a cero

¿Qué es ARC?

ARC (Automatic Reference Counting) es un mecanismo de gestión de memoria basado en compilador introducido por Apple en Xcode 4.2 (2011) para Objective-C y heredado por Swift. A diferencia de la gestión manual de memoria (Manual Retain-Release, MRR), ARC automatiza completamente las llamadas retain, release y autorelease, insertándolas en tiempo de compilación sin intervención del desarrollador.

ARC no es un recolector de basura. Es análisis estático con inserción dinámica de código: el compilador analiza los tiempos de vida de los objetos y coloca retain/release en los puntos donde los objetos se crean, copian o salen del ámbito. El resultado es una liberación determinista de memoria: el objeto se elimina exactamente cuando ya no hay referencias apuntando a él, sin demoras ni pausas.

Según la WWDC 2011 Session 323, la transición de MRR a ARC redujo los errores relacionados con la memoria en un 70% en las aplicaciones de Apple. Los desarrolladores dejaron de equilibrar manualmente retain/release, eliminando toda una clase de fugas y errores de double-free.

Cómo funciona Automatic Reference Counting

Cada objeto en memoria tiene un contador de referencias (retain count). Cuando se crea un objeto, el contador se establece en 1. Cuando una nueva referencia strong apunta al objeto, el contador aumenta (retain). Cuando una referencia strong desaparece, el contador disminuye (release). Al llegar a cero, el objeto se libera inmediatamente.

El compilador de Swift inserta retain/release no en cada asignación: utiliza análisis estático para optimizar. Por ejemplo, si se garantiza que un objeto no se usará después de ser pasado, el compilador puede omitir un release/retain innecesario. Esta optimización se llama ARC Optimization.

swift
class Person {
    let name: String
    init(name: String) {
        self.name = name
        print("\(name) initialized (retain count: 1)")
    }
    deinit {
        print("\(name) deallocated")
    }
}

func testARC() {
    let p = Person(name: "Alice")  // retain count = 1
    let q = p                      // retain count = 2
    // q sale del ámbito
    // retain count = 1
    // p sale del ámbito
    // retain count = 0 → deinit
}

Este ejemplo muestra cómo ARC gestiona el contador: al asignar q = p, el contador aumenta; cuando q sale del ámbito, disminuye. Cuando desaparece la última referencia strong, el desinicializador se llama inmediatamente. Ningún recolector de basura espera — la memoria se libera al instante.

ARC vs Garbage Collection: diferencias clave

ARC y Garbage Collection resuelven el mismo problema — la gestión automática de memoria — pero con enfoques fundamentalmente diferentes. La elección entre ellos define la arquitectura del lenguaje: Swift (ARC) vs Java/Go (GC). Veamos las principales diferencias.

CaracterísticaARC (Swift/ObjC)GC (Java/Go)
Momento de liberaciónDeterminista: inmediatamente al llegar el contador a ceroNo determinista: en el próximo ciclo de recolección
Pausas de ejecuciónNinguna (retain/release insertados en compilación)Pausas Stop-The-World (2–200 ms)
SobrecargaIncremento/decremento del contador en cada referenciaRecorrido del grafo de objetos, marcado, liberación
ProblemasRetain Cycle (resolución manual)Fragmentación del heap, fugas por referencias olvidadas
Hilo adicionalNo requiereRequiere hilo de recolección de basura

El compromiso clave: ARC proporciona tiempos de vida predecibles y cero pausas, pero requiere que el desarrollador entienda los retain cycles y elija correctamente weak/unowned. GC libera al desarrollador de estas preocupaciones, pero a costa de pausas no deterministas y un hilo adicional.

Strong, Weak y Unowned: calificadores de referencia en ARC

ARC define tres tipos de calificadores de referencia, cada uno afecta el contador y el ciclo de vida del objeto de manera diferente. Elegir el calificador correcto es la base de la gestión segura de memoria en Swift.

Strong

Strong es el calificador por defecto. Cada referencia strong aumenta el retain count del objeto en 1. Mientras exista al menos una referencia strong, el objeto permanece vivo. Todas las propiedades de clase y variables locales en Swift son strong por defecto. Las referencias strong crean una relación de propiedad: el objeto A posee al objeto B.

Weak

Weak es una referencia que no aumenta el retain count. Un objeto puede ser liberado incluso si una referencia weak apunta a él. Después de la liberación, la referencia weak se establece automáticamente a nil. Las referencias weak siempre se declaran como var con tipo opcional (?). Se usan para romper retain cycles, especialmente en el patrón delegate.

Unowned

Unowned es una referencia no propietaria que, como weak, no aumenta el retain count. Sin embargo, una referencia unowned no se establece a nil después de la liberación — acceder a un objeto liberado causa un crash. Unowned se usa cuando se garantiza que el objeto vive al menos tanto como el objeto referenciante. Los casos típicos son closures y relaciones padre-hijo con tiempo de vida garantizado.

swift
class Customer {
    let name: String
    var card: CreditCard?         // strong
    init(name: String) { self.name = name }
    deinit { print("\(name) deallocated") }
}

class CreditCard {
    let number: String
    unowned let customer: Customer   // unowned — no posee
    init(number: String, customer: Customer) {
        self.number = number
        self.customer = customer
    }
    deinit { print("Card \(number) deallocated") }
}

var customer: Customer? = Customer(name: "Bob")
customer?.card = CreditCard(number: "1234", customer: customer!)
customer = nil
// Customer y CreditCard ambos liberados — sin retain cycle

Aquí CreditCard usa una referencia unowned a Customer. Customer posee la tarjeta (strong), y la tarjeta no posee al cliente (unowned). Cuando Customer se libera, ambos objetos se liberan — no se produce retain cycle. Si card.customer fuera strong, el ciclo bloquearía la liberación.

Problemas comunes de ARC y sus soluciones

A pesar de la automatización, ARC no es una panacea. Los desarrolladores se encuentran con varios problemas típicos que requieren comprensión del mecanismo interno de gestión de memoria.

Retain Cycle en Closures

Las closures en Swift capturan variables externas por referencia strong. Si una closure se asigna a una propiedad de clase y captura self — se produce un retain cycle: la clase retiene la closure, la closure retiene self. La solución es una lista de captura con weak o unowned.

swift
class NetworkManager {
    var completionHandler: ((Data?) -> Void)?
    var data: Data?

    func fetchData() {
        completionHandler = { [weak self] result in
            guard let self else { return }
            self.data = result
            self.processResult()
        }
    }

    func processResult() { }
}

La lista de captura [weak self] crea una referencia weak a self dentro de la closure. Esto rompe el potencial retain cycle. Guard let self garantiza que el objeto está vivo antes de ejecutar el código. weak self es la práctica estándar para closures asíncronas en Swift.

Rendimiento de retain/release

Aunque retain/release son operaciones ligeras, en bucles intensivos los incrementos/decrementos frecuentes del contador añaden sobrecarga. En Swift 5.9+, el compilador utiliza optimización que elimina retain/release redundantes si el analizador demuestra que es seguro. Sin embargo, en Objective-C, retain/release todavía pueden ser un cuello de botella en escenarios de alta carga con millones de llamadas por segundo.

Autorelease Pool

Autorelease Pool es un mecanismo de liberación diferida utilizado en Objective-C y algunos escenarios de Swift. Los objetos se colocan en el pool y reciben release cuando el pool se vacía. En bucles con muchos objetos temporales (por ejemplo, análisis JSON), crear un autoreleasepool personalizado reduce el consumo máximo de memoria.

Preguntas frecuentes

¿En qué se diferencia ARC de la gestión manual de memoria (MRR)?

En la gestión manual (MRR), el desarrollador llamaba explícitamente a retain, release y autorelease. ARC inserta estas llamadas automáticamente en tiempo de compilación, eliminando el riesgo de double-free, fugas por release olvidado y errores de equilibrio retain/release.

¿Puede ARC funcionar con código C/C++?

ARC gestiona solo objetos Objective-C y clases Swift. Para estructuras y punteros C/C++, ARC no se aplica — estos objetos se gestionan manualmente o mediante smart pointers de C++ (shared_ptr, unique_ptr). Los objetos Core Foundation (CFString, CGColor) tampoco están cubiertos por ARC.

¿Cuándo usar weak y cuándo unowned?

weak — cuando el objeto puede liberarse antes que el objeto referenciante (delegados, closures asíncronas). unowned — cuando se garantiza que el objeto vive al menos tanto como el referenciante (padre-hijo donde el hijo no puede existir sin el padre). Si no estás seguro, elige weak.

¿Qué son los tipos existenciales y cómo afectan a ARC?

Los tipos existenciales (protocol como tipo) en Swift envuelven el valor en un contenedor especial (contenedor existencial). Esto aumenta el número de retain/release en los límites de los protocolos. En Swift 5.7+, los tipos de resultado opaco y los parámetros some reducen la sobrecarga al eliminar el contenedor.

¿Cómo verificar el retain count en Swift?

No hay una API directa para leer el retain count en Swift — se considera un detalle de implementación. Para diagnóstico, usa Instruments (Allocations, Leaks) o el Depurador de Memoria en Xcode. Estas herramientas muestran el número de instancias de clase vivas y las cadenas de retención.

Resumen

  • ARC — sistema de gestión de memoria basado en compilador para Swift y Objective-C que funciona mediante conteo de referencias
  • Principio — cada objeto tiene un retain count; al llegar a cero, el objeto se libera inmediata y deterministicamente
  • Diferencia de GC — ARC funciona sin hilo en segundo plano ni pausas Stop-The-World, pero requiere control de retain cycles
  • Strong — aumenta el contador; weak y unowned no lo aumentan, pero unowned no se anula al liberarse
  • Closures — la principal causa de retain cycles en Swift; la lista de captura [weak self] es la solución estándar
  • Autorelease Pool — mecanismo de liberación diferida para objetos temporales en bucles y escenarios personalizados
  • Diagnóstico — Xcode Memory Debugger, Instruments y LeakCanary (mediante puente ObjC) para encontrar problemas

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