2009-07-18 13 views
5

He estado revisando las preguntas formuladas en StackOverflow, pero hay tantas sobre la administración de memoria en Objective-C que no pude encontrar la respuesta que estaba buscando.¿Usar la liberación automática antes de agregar objetos a una colección?

La pregunta es si está bien (y lo recomiendo) llamar a la liberación automática antes de agregar un objeto recién creado a una colección (como NSMutableArray)? O debería liberarlo explícitamente después de agregarlo. (Sé NSMutableArray willl retener el objeto)

Esto ilustra mi pregunta:

Escenario A (autorelease):

- (void) add { 
    // array is an instance of NSMutableArray 

    MyClass *obj = [[[MyClass alloc] init] autorelease]; 

    [array addObject:obj]; 
} 

Escenario B (liberación explícita):

- (void) add { 
    // array is an instance of NSMutableArray 

    MyClass *obj = [[MyClass alloc] init]; 

    [array addObject:obj]; 

    [obj release]; 
} 

Supongo que ambos son correctos, pero no estoy seguro, y estoy seguro de que no sé cuál es la forma preferida.

¿Pueden los gurús de Objective-C arrojar algo de luz sobre esto?

Respuesta

7

Ambos son correctos y funcionarán como usted espera.

Personalmente prefiero usar el último método, pero solo porque me gusta ser explícito sobre cuándo se lanzan los objetos. Al liberar automáticamente el objeto, todo lo que hacemos es decir "este objeto se lanzará en algún punto arbitrario en el futuro". Eso significa que puede poner el objeto liberado automáticamente en la matriz, destruir la matriz y el objeto (probablemente) todavía existe.

Con este último método, el objeto se destruirá inmediatamente con la matriz (siempre que nada más haya aparecido y se haya conservado mientras tanto). Si estoy en un entorno con restricciones de memoria (por ejemplo, el iPhone) donde tengo que tener cuidado con la cantidad de memoria que estoy usando, usaré el último método solo para no tener tantos objetos que permanezcan en él. un NSAutoreleasePool en alguna parte. Si el uso de la memoria no es una gran preocupación para usted (y tampoco suele serlo para mí), cualquier método es totalmente aceptable.

+2

Si desea que su código sea absolutamente seguro, se recomienda encarecidamente utilizar la liberación automática: consulte http://stackoverflow.com/questions/1147785/use-autorelease-before-adding-objects-to-a-collection/1149040#1149040. – Casebash

3

Ambos son correctos, pero B puede ser preferible porque no tiene ningún tipo de sobrecarga. Autorelease hace que el grupo de autorrelease se haga cargo del objeto. Esto tiene una sobrecarga muy leve que, por supuesto, se multiplica por la cantidad de objetos involucrados.

Así que con un objeto A y B son más o menos lo mismo, pero definitivamente no use A en escenarios con muchos objetos para agregar a la matriz.

En diferentes situaciones, el autorrelleno puede retrasar y acumular la liberación de muchos objetos al final del hilo. Esto puede ser subóptimo. Tenga cuidado de que de todos modos la liberación automática ocurra mucho sin una intervención explícita. Por ejemplo, muchos captadores se implementan esta manera:

return [[myObject retain] autorelease]; 

así que cuando se llama al captador se agrega un objeto a la piscina autorelease.

+0

"Autorelease hace que el grupo de autorrelease se haga cargo del objeto. " Esto es incorrecto. El grupo de autorrelease no es más que un recurso de mensajería retrasada. El grupo de autorrelease no "se hace cargo" de un objeto, solo lo enumera para recibir un mensaje de liberación en el futuro. – NSResponder

+0

Con "tomar las riendas" quise decir exactamente eso. Nací en la UE, pero no en el Reino Unido, es mi culpa. – IlDan

0

Tienes alloc 'ed el objeto, entonces es su trabajo para liberarlo en algún momento. Ambos fragmentos de código funcionan simplemente de la misma manera, con la forma autorelease siendo la contraparte potencialmente más lenta.

Personalmente hablando, prefiero la forma autorelease, ya que es simplemente más fácil de escribir y casi nunca es un cuello de botella.

0

Ambos están bien. Algunas personas te dirán que evites la liberación automática por "sobrecarga" o algo por el estilo, pero la verdad es que prácticamente no hay sobrecarga. Continúa y compara el punto e intenta encontrar el "overhead". La única razón por la que lo evitarías es en una situación de falta de memoria como en el iPhone. En OS X, tiene memoria prácticamente ilimitada, por lo que no va a hacer mucha diferencia. Simplemente use el que sea más conveniente para usted.

+1

No es tan simple como eso. Si los objetos se liberan automáticamente en un bucle, es posible que queden grandes cantidades de objetos extra hasta que se vacíe un conjunto de autorretención. Esto a veces puede causar grandes sobrecargas de memoria y ralentizaciones. (Sí, me ha pasado a mí.) Esto no es tan común, sin embargo. Por lo general, puede ignorar el problema hasta que suceda, y luego observar lo que está sucediendo en el ciclo y optimizarlo mediante el uso de versiones explícitas. –

+1

Todo lo que se requiere es una administración especial del grupo de autorrelease. Todavía no significa que la liberación automática sea algo malo, especialmente no en el caso general. – Chuck

11

En mi humilde opinión, qué camino es 'correcto' es una cuestión de preferencia. No estoy en desacuerdo con los respondedores que abogan por no usar autorelease, pero mi preferencia es usar autorelease a menos que exista una razón abrumadoramente convincente para no hacerlo. Enumeraré mis razones y podrá decidir si son apropiadas o no a su estilo de programación.

Como señaló Chuck, existe una leyenda semiurbana de que hay algún tipo de sobrecarga en el uso de grupos de autorreleases. Esto no podría estar más lejos de la verdad, y esto viene de incontables horas dedicadas a usar Shark.app para sacar el último rendimiento del código. Intentar optimizar esto se encuentra en el territorio de la "optimización prematura". Si, y solo si, Shark.app te proporciona datos concretos, esto podría ser un problema, incluso si consideras buscarlo.

Como han señalado otros, un objeto liberado automáticamente se "libera en algún momento posterior". Esto significa que se quedan, ocupando la memoria, hasta que ese "punto posterior" rueda. Para la mayoría de los casos, esto se encuentra en la parte inferior de un pase de procesamiento de eventos antes de que el bucle de ejecución duerma hasta el próximo evento (temporizador, usuario que hace clic en algo, etc.).

Ocasionalmente, sin embargo, tendrá que deshacerse de esos objetos temporales más pronto, en lugar de más tarde. Por ejemplo, debe procesar un archivo enorme de varios megabytes o decenas de miles de filas de una base de datos. Cuando esto suceda, deberá colocar un NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; en un punto bien elegido, seguido de un [pool release]; en la parte inferior. Esto casi siempre sucede en algún tipo de "procesamiento por lotes de bucle", por lo que suele ser al principio y al final de algún bucle crítico. Una vez más, esto debe basarse en la evidencia, no en la base del presentimiento. El ObjectAlloc de Instrument.app es lo que utilizas para encontrar estos puntos problemáticos.

La razón principal por la que prefiero autorelease a release, sin embargo, es que es mucho más más fácil escribir programas libres de fugas. En resumen, si usted decide ir a la ruta release, es necesario garantía que release se envía finalmente a obj, bajo todos circunstancias. Si bien esto parece que podría ser simple, en realidad es sorprendentemente difícil de hacer en la práctica. Tomar su ejemplo, por ejemplo:

// array is an instance of NSMutableArray 
    MyClass *obj = [[MyClass alloc] init]; 
    [array addObject:obj]; 
    // Assume a few more lines of work.... 
    [obj release]; 

Ahora imaginemos que, por alguna razón, algo, en alguna parte, sutilmente viola el supuesto de que array es mutable, tal vez como resultado de la utilización de algún método para procesar los resultados, y la devuelve array que contiene los resultados procesados ​​fue creado como NSArray.Cuando envíe addObject: a ese inmutable NSArray, se lanzará una excepción y nunca enviará obj su mensaje release. O tal vez algo va mal en alguna parte entre el momento en obj fue alloc dy el requiere llamada a release, como se comprueba alguna condición y return() inmediatamente por error, ya que deslizó su mente que esa llamada a release más adelante debe tener lugar.

Acaba de filtrar un objeto. Y probablemente se haya registrado durante varios días tratando de descubrir dónde y por qué lo está filtrando. Por experiencia, pasará muchas horas mirando ese código anterior, convencido de que posiblemente no sea la fuente de la fuga porque muy claramente envía obj a release. Luego, después de varios días, experimentarás lo que solo se puede describir como una epifanía religiosa a medida que te iluminen con la causa del problema.

Consideremos el caso autorelease:

// array is an instance of NSMutableArray 
    MyClass *obj = [[[MyClass alloc] init] autorelease]; 
    [array addObject:obj]; 
    // Assume a few more lines of work.... 

Ahora, ya no importa lo que pase, porque es prácticamente imposible escapar obj accidentalmente, incluso en casos de esquina extremadamente inusuales o excepcionales.

+0

Esto surgió en mi nueva pregunta aquí: http://stackoverflow.com/questions/2911678. Gran punto –

+1

'[pool drain]' parece la mejor opción en el caso de que iOS obtenga GC –

0

Prefiero A (autorrelleno) por brevedad y "seguridad", como lo llama johne. Simplifica mi código, y nunca me he encontrado con problemas.

Es decir, hasta hoy: tuve un problema con la liberación automática de un bloque antes de agregarlo a una matriz. Ver mi pregunta stackoverflow: [myArray addObject:[[objcBlock copy] autorelease]] crashes on dealloc'ing the array (Actualización: Resulta que el problema estaba en otra parte de mi código, pero aún así, había una diferencia sutil en el comportamiento con liberación automática ...)

Cuestiones relacionadas