Pregunta

Quería aclarar algo.

Digamos que tengo el siguiente código:

- (void) viewDidAppear:(BOOL)animated {
  [super viewDidAppear:animated];
  for (int i = 0; i < 5000000; i++) {
    NSString *s = [NSString stringWithFormat:@"Hello, %@!", @"World"];
  }
}

Esto creará 5 millones de cadenas autoreladas dentro de esta llamada de función. Esperaba que esto mantuviera esos objetos alrededor hasta la terminación de la aplicación, ya que el único @autoreleasepool que veo es el que envuelve la instancia de la aplicación en Main.m. Sin embargo, ese no es el caso. Al final de esta llamada de función, parece que todos reciben su liberación y se eliminan de la memoria.

Este documento:

https://developer.apple.com/library/mac/documentation/cocoa/reference/foundation/classes/nsautoreleasepool_class/reference/reference.html

Establece que "el kit de aplicaciones crea un grupo de autoresas en el hilo principal al comienzo de cada ciclo del bucle de eventos, y lo drena al final, liberando así los objetos autorelados generados mientras procesan un evento".

Eso tiene sentido para mí, pero esto está bajo UIKIT, y no el kit de aplicaciones. Mi pregunta es, ¿Uikit/Cocoa Touch hace lo mismo en este caso, o hay otra explicación para que mis objetos sean lanzados?

¡Gracias!

¿Fue útil?

Solución 2

Sí, Uikit hace lo mismo. La piscina de autorrease de subprocesos principal creado por el sistema está drenado al final de cada ciclo de bucle de ejecución. Probablemente sea mejor no confiar en esta vida exacta en su propio código. Si crea un nuevo hilo manualmente (usando EG NSTHREAD), usted es responsable de crear el grupo de Autorelease en ese hilo.

EDITAR: La respuesta de Rob proporciona información adicional sobre el comportamiento bajo ARC. En general, es justo decir que los objetos tienen menos probabilidades de terminar en el grupo de autoresas debido a algunas optimizaciones que Arc puede hacer.

Otros consejos

Andrew respondió a su pregunta principal de que, sí, su grupo de autoresas se agotará en cada ciclo del ciclo de ejecución principal. Entonces, cualquier objeto de autora de ejecución creado en viewDidLoad Puede drenarse de inmediato cuando vuelva a ceder al bucle de ejecución principal. Ciertamente no serán retenidos "hasta la terminación de la aplicación".

Pero debemos tener cuidado: está claramente asumiendo que estos objetos se están agregando a un grupo de autoresas. Algunas advertencias a esta suposición:

  1. En el pasado (y aún requerido para la interoperabilidad de ARC-MRC), al devolver objetos de los métodos cuyos nombres no comenzaron con alloc, new, copy, o mutableCopy, esos objetos se activarían los objetos de autorrealización, desaconsejados solo cuando se drenó el grupo de autoresas (es decir, cuando cedió al ciclo de ejecución).

  2. Pero ARC se ha vuelto más inteligente sobre minimizar la necesidad de grupos de autoraselase (ver http://rentzsch.tumblr.com/post/75082194868/arcs-stast-eutorelease, que discute callerAcceptsFastAutorelease, ahora llamado callerAcceptsOptimizedReturn invocado por prepareOptimizedReturn), por lo que a menudo no verás esto autorelease comportamiento. Por lo tanto, si tanto la biblioteca como la persona que llama usa ARC, los objetos no pueden colocarse en el grupo de autorlease, sino que ARC los liberará inteligentemente de inmediato si no son necesarios.

    Con los proyectos de ARC contemporáneos, las piscinas de autorhease generalmente no son necesarias. Pero ciertos casos especiales, uno aún puede beneficiarse del uso de grupos de autoresas. Cumpliré uno de esos casos a continuación.

Considere el siguiente código:

#import "ViewController.h"
#import <sys/kdebug_signpost.h>

typedef enum : NSUInteger {
    InnerLoop = 1,
    MainLoop = 2
} MySignPostCodes;

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSURL *fileURL = [[NSBundle mainBundle] URLForResource:@"test" withExtension:@"png"];
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        kdebug_signpost_start(MainLoop, 0, 0, 0, 1);
        for (int j = 0; j < 500; i++) {
            NSData *data = [NSData dataWithContentsOfURL:fileURL];
            UIImage *image = [[UIImage alloc] initWithData:data];
            NSLog(@"%p", NSStringFromCGSize(image.size));  // so it's not optimized out
            [NSThread sleepForTimeInterval:0.01];
        }
        kdebug_signpost_end(MainLoop, 0, 0, 0, 1);
    });
}

@end

El siguiente código agregará 500,000 objetos al grupo de Autorelease, que solo se drenará cuando vuelva al ciclo de ejecución:

no pool

En este caso, puede usar una piscina de autoresas para minimizar la marca de agua alta:

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSURL *fileURL = [[NSBundle mainBundle] URLForResource:@"test" withExtension:@"png"];
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        kdebug_signpost_start(MainLoop, 0, 0, 0, 1);
        for (int j = 0; j < 5; j++) {
            @autoreleasepool {
                kdebug_signpost_start(InnerLoop, 0, 0, 0, 2);
                for (long i = 0; i < 100; i++) {
                    NSData *data = [NSData dataWithContentsOfURL:fileURL];
                    UIImage *image = [[UIImage alloc] initWithData:data];
                    NSLog(@"%p", NSStringFromCGSize(image.size));  // so it's not optimized out
                    [NSThread sleepForTimeInterval:0.01];
                }
                kdebug_signpost_end(InnerLoop, 0, 0, 0, 2);
            }
        }
        kdebug_signpost_end(MainLoop, 0, 0, 0, 1);
    });
}

@end

pool

En pocas palabras, con ARC, no siempre es obvio cuando usó un objeto de autorazones y cuando lo libera explícitamente cuando la variable cae fuera del alcance. Siempre puede confirmar esto examinando el comportamiento en los instrumentos.

Como aparte, sería cauteloso al sacar demasiadas conclusiones generales de gestión de la memoria al usar el NSString Clase, ya que ha sido altamente optimizado y no siempre se ajusta a las prácticas de gestión de memoria estándar.

Supongo que cuando asigna un nuevo objeto a una referencia que solía retener un objeto entonces El objeto original se lanza de inmediato (Si nada más lo señala: el recuento de refer se acerca a cero) usando ARC y ASUMING predeterminado strong referencia como en su ejemplo de bucle.

MyObject *object = [[MyObject alloc] init]; // obj1, ref count 1 because strong
object = [[MyObject alloc] init]; // obj2, ref count of obj1 should be 0
                                  // so obj1 gets released

Notas de manzana en Transición a las notas de lanzamiento de arco

Intente dejar de pensar en dónde se realizan las llamadas de retención/lanzamiento y piense en los algoritmos de su aplicación. Piense en punteros "fuertes y débiles" en sus objetos, en la propiedad de objetos y en posibles ciclos de retención.

Suena que el release se llama al objeto cuando se le asigna un nuevo valor, desde Clang CLANG 3.4 Documentación Objetivo-C Contado de referencia automática (ARC)

La asignación ocurre al evaluar un operador de asignación. La semántica varía según la calificación:

Para los objetos __strong, el nuevo Pineo se retiene primero; En segundo lugar, el Lvalue está cargado con semántica primitiva; En tercer lugar, el nuevo Pineo se almacena en el Lvalue con semántica primitiva; Y finalmente, se libera el viejo puntee. Esto no se realiza atómicamente; La sincronización externa debe usarse para hacer que esto sea seguro frente a las cargas y tiendas concurrentes.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top