Gestión de memoria Obj-C

La gestión de la memoria es uno de los procesos más importantes en cualquier lenguaje de programación. Es el proceso mediante el cual la memoria de los objetos se asigna cuando se requieren y se desasigna cuando ya no se requieren.

La gestión de la memoria de objetos es una cuestión de rendimiento; si una aplicación no libera objetos innecesarios, su huella de memoria aumenta y el rendimiento se ve afectado.

Las técnicas de gestión de memoria de Objective-C se pueden clasificar en dos tipos.

  • "Retención-liberación manual" o MRR
  • "Recuento automático de referencias" o ARC

"Retención-liberación manual" o MRR

En MRR, gestionamos explícitamente la memoria realizando un seguimiento de los objetos por nuestra cuenta. Esto se implementa mediante un modelo, conocido como recuento de referencias, que la clase Foundation NSObject proporciona junto con el entorno de ejecución.

La única diferencia entre MRR y ARC es que la retención y liberación es manejada por nosotros manualmente en el primero, mientras que se realiza automáticamente en el segundo.

La siguiente figura representa un ejemplo de cómo funciona la administración de memoria en Objective-C.

El ciclo de vida de la memoria del objeto de Clase A se muestra en la figura anterior. Como puede ver, el recuento de retención se muestra debajo del objeto, cuando el recuento de retención de un objeto se vuelve 0, el objeto se libera por completo y su memoria se desasigna para que otros objetos la usen.

El objeto de clase A se crea primero utilizando el método alloc / init disponible en NSObject. Ahora, el recuento de retención se convierte en 1.

Ahora, la clase B retiene el objeto de la clase A y el recuento de retención del objeto de la clase A se convierte en 2.

Luego, la Clase C hace una copia del objeto. Ahora, se crea como otra instancia de Clase A con los mismos valores para las variables de instancia. Aquí, el recuento de retención es 1 y no el recuento de retención del objeto original. Esto está representado por la línea de puntos en la figura.

El objeto copiado es liberado por la Clase C usando el método de liberación y el conteo de retención se vuelve 0 y por lo tanto el objeto es destruido.

En el caso del Objeto de Clase A inicial, el recuento de retención es 2 y debe liberarse dos veces para que se destruya. Esto se hace mediante declaraciones de liberación de Clase A y Clase B que reducen el recuento de retención a 1 y 0, respectivamente. Finalmente, el objeto se destruye.

Reglas básicas de MRR

  • Somos dueños de cualquier objeto que creamos: Creamos un objeto usando un método cuyo nombre comienza con "alloc", "new", "copy" o "mutableCopy"

  • Podemos tomar posesión de un objeto usando retener: normalmente se garantiza que un objeto recibido seguirá siendo válido dentro del método en el que se recibió, y ese método también puede devolver el objeto de forma segura a su invocador. Usamos retener en dos situaciones:

    • En la implementación de un método de acceso o un método de inicio, para tomar posesión de un objeto queremos almacenar como valor de propiedad.

    • Para evitar que un objeto se invalide como efecto secundario de alguna otra operación.

  • Cuando ya no lo necesitamos, debemos renunciar a la propiedad de un objeto que poseemos: renunciamos a la propiedad de un objeto enviándole un mensaje de liberación o un mensaje de liberación automática. Por lo tanto, en la terminología de Cocoa, renunciar a la propiedad de un objeto se denomina normalmente "liberar" un objeto.

  • No debe renunciar a la propiedad de un objeto que no es de su propiedad: esto es solo un corolario de las reglas de política anteriores establecidas explícitamente.

#import <Foundation/Foundation.h>

@interface SampleClass:NSObject
- (void)sampleMethod;
@end

@implementation SampleClass
- (void)sampleMethod {
   NSLog(@"Hello, World! \n");
}

- (void)dealloc  {
  NSLog(@"Object deallocated");
  [super dealloc];
}

@end

int main() {
   
   /* my first program in Objective-C */
   SampleClass *sampleClass = [[SampleClass alloc]init];
   [sampleClass sampleMethod];
   
   NSLog(@"Retain Count after initial allocation: %d", 
   [sampleClass retainCount]);
   [sampleClass retain];
   
   NSLog(@"Retain Count after retain: %d", [sampleClass retainCount]);
   [sampleClass release];
   NSLog(@"Retain Count after release: %d", [sampleClass retainCount]);
   [sampleClass release];
   NSLog(@"SampleClass dealloc will be called before this");
   
   // Should set the object to nil
   sampleClass = nil;
   return 0;
}

Cuando compilemos el programa anterior, obtendremos el siguiente resultado.

2013-09-28 04:39:52.310 demo[8385] Hello, World!
2013-09-28 04:39:52.311 demo[8385] Retain Count after initial allocation: 1
2013-09-28 04:39:52.311 demo[8385] Retain Count after retain: 2
2013-09-28 04:39:52.311 demo[8385] Retain Count after release: 1
2013-09-28 04:39:52.311 demo[8385] Object deallocated
2013-09-28 04:39:52.311 demo[8385] SampleClass dealloc will be called before this

"Recuento automático de referencias" o ARC

En el conteo automático de referencias o ARC, el sistema usa el mismo sistema de conteo de referencias que MRR, pero inserta las llamadas al método de administración de memoria apropiado para nosotros en tiempo de compilación. Se nos anima encarecidamente a utilizar ARC para nuevos proyectos. Si usamos ARC, normalmente no es necesario comprender la implementación subyacente descrita en este documento, aunque en algunas situaciones puede ser útil. Para obtener más información sobre ARC, consulte las Notas de la versión de transición a ARC.

Como se mencionó anteriormente, en ARC, no necesitamos agregar métodos de liberación y retención, ya que de eso se ocupará el compilador. En realidad, el proceso subyacente de Objective-C sigue siendo el mismo. Utiliza las operaciones de retención y liberación internamente, lo que facilita al desarrollador la codificación sin preocuparse por estas operaciones, lo que reducirá tanto la cantidad de código escrito como la posibilidad de pérdidas de memoria.

Había otro principio llamado recolección de basura, que se usa en Mac OS-X junto con MRR, pero desde su desaprobación en OS-X Mountain Lion, no se ha discutido junto con MRR. Además, los objetos de iOS nunca tuvieron la función de recolección de basura. Y con ARC, tampoco se utiliza la recolección de basura en OS-X.

Aquí hay un ejemplo simple de ARC. Tenga en cuenta que esto no funcionará en el compilador en línea ya que no es compatible con ARC.

#import <Foundation/Foundation.h>

@interface SampleClass:NSObject
- (void)sampleMethod;
@end

@implementation SampleClass
- (void)sampleMethod {
   NSLog(@"Hello, World! \n");
}

- (void)dealloc  {
  NSLog(@"Object deallocated");
}

@end

int main() {
   /* my first program in Objective-C */
   @autoreleasepool {
      SampleClass *sampleClass = [[SampleClass alloc]init];
      [sampleClass sampleMethod];
      sampleClass = nil;
   }
   return 0;
}

Cuando compilemos el programa anterior, obtendremos el siguiente resultado.

2013-09-28 04:45:47.310 demo[8385] Hello, World!
2013-09-28 04:45:47.311 demo[8385] Object deallocated