¿Cómo funciona el grupo de liberación automática de NSAutoreleasePool?

95

Según tengo entendido, todo lo que se cree con una asignación , un nuevo o una copia debe liberarse manualmente. Por ejemplo:

int main(void) {
    NSString *string;
    string = [[NSString alloc] init];
    /* use the string */
    [string release];
}

Mi pregunta, sin embargo, ¿no sería esto igualmente válido ?:

int main(void) {
    NSAutoreleasePool *pool;
    pool = [[NSAutoreleasePool alloc] init];
    NSString *string;
    string = [[[NSString alloc] init] autorelease];
    /* use the string */
    [pool drain];
}
James Sumners
fuente

Respuestas:

68

Sí, su segundo fragmento de código es perfectamente válido.

Cada vez que se envía -autorelease a un objeto, se agrega al grupo de autorelease más interno. Cuando se drena la piscina, simplemente envía un lanzamiento a todos los objetos de la piscina.

Los grupos de liberación automática son simplemente una conveniencia que le permite diferir el envío de la liberación hasta "más tarde". Ese "más tarde" puede suceder en varios lugares, pero lo más común en las aplicaciones Cocoa GUI es al final del ciclo de ejecución actual.

kperryua
fuente
5
¿Dónde está el final del ciclo de ciclo de ejecución actual, si no tengo un ciclo?
Gracias
24
¿No debería ser "más externo" "más interno"?
Mike Weller
an objectdebería ser an object that is a subclass of NSObject or NSProxy and doesn't override -autorelease.
1
EDITAR: Se cambió más externo a más interno.
chakrit
1
Importante: si usa el conteo automático de referencias (ARC), no puede usar grupos de liberación automática directamente. En su lugar, usa bloques @autoreleasepool. De developer.apple.com/library/mac/#documentation/Cocoa/Reference/…
Md Mahbubur Rahman
37

NSAutoreleasePool: drenaje frente a liberación

Dado que la función de drainy releaseparece estar causando confusión, puede valer la pena aclararlo aquí (aunque esto está cubierto en la documentación ...).

Estrictamente hablando, desde la perspectiva del panorama general nodrain es equivalente a :release

En un entorno contado por referencias, drain realiza las mismas operaciones que release, por lo que los dos son en ese sentido equivalentes. Para enfatizar, esto significa que no filtra una piscina si usa en drainlugar derelease .

En un entorno de recolección de basura, no releasees una operación. Por tanto, no tiene ningún efecto. drain, por otro lado, contiene una sugerencia para el coleccionista de que debe "recopilar si es necesario". Por lo tanto, en un entorno de recolección de basura, el uso drainayuda al sistema a equilibrar los barridos de recolección.

mmalc
fuente
4
Es fundamentalmente imposible "filtrar" a NSAutoreleasePool. Esto se debe a que las agrupaciones funcionan como una pila. La creación de instancias de un grupo empuja ese grupo a la parte superior de la pila de grupos de liberación automática de subprocesos. -releasehace que ese grupo salga de la pila Y cualquier grupo que se colocó encima de él, pero que por alguna razón no se abrieron.
johne
7
¿De qué manera esto es relevante para lo que escribí?
mmalc
2
Me gusta cómo se tomó el tiempo para atreverse Y. ¡CHASQUIDO!
Billy Gray
17

Como ya se señaló, su segundo fragmento de código es correcto.

Me gustaría sugerir una forma más sucinta de usar el grupo de liberación automática que funciona en todos los entornos (recuento de referencias, GC, ARC) y también evita la confusión de drenaje / liberación:

int main(void) {
  @autoreleasepool {
    NSString *string;
    string = [[[NSString alloc] init] autorelease];
    /* use the string */
  }
}

En el ejemplo anterior, tenga en cuenta el bloque @autoreleasepool . Esto está documentado aquí .

Neovibrante
fuente
2
Tenga en cuenta que la liberación automática no está permitida con ARC.
dmirkitanov
1
Para aclarar, hay que usar el @autoreleasepoolbloque con ARC.
Simon
7

No, tu estas equivocado. La documentación establece claramente que, en caso de que no sea GC, -drain es equivalente a -release, lo que significa que NSAutoreleasePool no se filtrará .

kperryua
fuente
Me preguntaba por qué Xcode generaría código con -drain si ese fuera el caso. Usé -drain porque pensé que era equivalente a -release basado en el código generado por Xcode.
James Sumners
1
Es fundamentalmente imposible 'filtrar' a NSAutoreleasePool: developer.apple.com/mac/library/documentation/Cocoa/Conceptual/…
johne
0

enviar la liberación automática en lugar de la liberación a un objeto extiende la vida útil de ese objeto al menos hasta que se agote la piscina (puede ser más larga si el objeto se retiene posteriormente). Un objeto se puede colocar en el mismo grupo varias veces, en cuyo caso recibe un mensaje de liberación cada vez que se coloca en el grupo.

Hardik Mamtora
fuente
-2

Si y no. Terminaría liberando la cadena de memoria pero "filtrando" el objeto NSAutoreleasePool en la memoria usando drenaje en lugar de liberación si ejecuta esto en un entorno de recolección de basura (no administrado por memoria). Esta "fuga" simplemente hace que la instancia de NSAutoreleasePool sea "inalcanzable" como cualquier otro objeto sin punteros fuertes debajo de GC, y el objeto se limpiará la próxima vez que se ejecute GC, lo que muy bien podría ser directamente después de la llamada a -drain:

desagüe

En un entorno de recolección de elementos no utilizados, activa la recolección de elementos no utilizados si la memoria asignada desde la última recolección es mayor que el umbral actual; de lo contrario se comporta como liberación. ... En un entorno de recolección de basura, este método finalmente llama objc_collect_if_needed.

De lo contrario, es similar a cómo se -releasecomporta sin GC, sí. Como han dicho otros, no -releasees una operación en GC, por lo que la única forma de asegurarse de que el grupo funcione correctamente en GC es a través -drain, y -drainen no GC funciona exactamente como-release en no GC, y podría decirse que comunica su funcionalidad más claramente como bien.

Debo señalar que su declaración "cualquier cosa llamada con new, alloc o init" no debe incluir "init" (pero debe incluir "copy"), porque "init" no asigna memoria, solo configura el objeto (constructor Moda). Si recibió un objeto asignado y su función solo llamó a init como tal, no lo liberaría:

- (void)func:(NSObject*)allocd_but_not_init
{
    [allocd_but_not_init init];
}

Eso no consume más memoria de la con la que ya comenzó (asumiendo que init no crea una instancia de los objetos, pero usted no es responsable de ellos de todos modos).

Loren Segal
fuente
No me siento cómodo dejando esta respuesta como aceptada cuando su información sobre el drenaje no es del todo correcta. Consulte developer.apple.com/documentation/Cocoa/Reference/Foundation/… Actualizar y volveré a aceptar.
James Sumners
¿Qué hay de inexacto en la respuesta? En un entorno de recolección de basura (como se indicó), el drenaje no elimina el AutoReleasePool, por lo que perderá memoria a menos que use release. La cita que enumeré fue directamente de la boca del caballo, los documentos sobre el drenaje.
Loren Segal
1
Loren: Bajo GC, - [NSAutoreleasePool Drain] activará una colección. -retain, -release y -autorelease son ignorados por el recopilador; es por eso que -drain se usa en grupos de liberación automática bajo GC.
Chris Hanson
En la documentación de 'drenaje': en un entorno de memoria administrada, esto se comporta de la misma manera que llamar a release. Por lo tanto, no perderá memoria si usa 'drenar' en lugar de liberar.
mmalc
-[NSAutoreleasePool release]en un entorno de recolección de basura no es una operación. -[NSAutoreleasePool drain]funciona tanto en entornos de recuento de referencias como de recolección de basura.
Jonathan Sterling