2009-08-02 11 views
25

Soy nuevo en el código administrado de memoria pero entiendo bastante bien la idea.Versión C del objetivo C, liberación automática y tipos de datos

Al llevar mi aplicación a través de la herramienta de fugas en XCode, noté que solo tenía que limpiar mis objetos personalizados, pero no las matrices creadas dinámicamente, por ejemplo, así que creí que esos tipos de datos se liberaban automáticamente. Tiene sentido ya que solo para liberar las matrices que utilicé como propiedades que tenían (retener) en ellas.

Entonces me di cuenta de algo extraño: me estaba poniendo una fuga en una cierta gama inicializado como esto:

NSMutableArray *removals = [NSMutableArray new]; 

pero no una similar

NSMutableArray *removals = [NSMutableArray arrayWithCapacity:9]; 

Ahora, la razón por la que se haya definido con "nuevo" es que podría tener de 0 a 99 elementos, mientras que el otro que sabía que iba a ser siempre 9. Dado que ambas matrices se pasan al mismo método más adelante en función de la interacción del usuario, o estaba obteniendo una fuga ¡Si no lo liberé al final del método, o una excepción si lo hiciera!

me cambió la primera matriz de

NSMutableArray *removals = [NSMutableArray arrayWithCapacity:99]; 

y me sale no hay fugas y no tienen nada para liberar. ¿Alguien puede explicar?

Respuesta

65

Como se señaló en el memory management rules, cada vez que tenga un objeto que ha creado con +alloc, +new, -copy o -mutableCopy, es el propietario y que es responsable de la liberación en algún momento. (De hecho, +new es solo una abreviatura de [[MyClass alloc] init].) Como ha indicado, crear una matriz a través de [NSArray new] sin soltarla es una pérdida de memoria.Sin embargo, si maneja este objeto correctamente, generalmente es posible liberarlo en algún momento. Por ejemplo:

  • Si el método que utiliza la matriz se llama desde dentro el método que crea la matriz, entonces usted debería ser capaz de liberar la matriz después de que se ha utilizado. Si el método interno necesita mantener una referencia más permanente a la matriz, entonces ese método es responsable de enviar -retain y, eventualmente, -release al objeto. Por ejemplo:

    - (void)myMethod { 
        NSArray *removals = [NSArray new]; 
        // ... 
        [someObject someOtherMethod:removals]; 
        [removals release]; 
    } 
    
  • Si ha creado la matriz en un método-init para un objeto, entonces el método -dealloc puede liberarlo cuando el objeto es destruido.

  • Si necesita crear la matriz y luego retorno que a partir del método, que ha descubierto la razón de que autoreleasing fue inventado. La persona que llama de su método no es responsable de liberar el objeto, ya que no es un método +alloc, +new, -copy, o -mutableCopy, pero debe asegurarse de que se libere con el tiempo. En este caso, llame manualmente al -autorelease en el objeto antes de devolverlo. Por ejemplo:

    - (NSArray *)myMethod { 
        NSArray *removals = [NSArray new]; 
        // ... 
        return [removals autorelease]; 
    } 
    

Cuando se crea la matriz a través de +arrayWithCapacity:, no está llamando a uno de los métodos "especiales", por lo que no tiene que liberar el resultado. Esto probablemente se implemente con -autorelease, al igual que el último ejemplo anterior, pero no necesariamente. (Por cierto, también puede crear un NSMutableArray autorellenado vacío con [NSMutableArray array]; el método se encuentra en NSArray, por lo que no aparecerá en la documentación en NSMutableArray, pero creará una matriz mutable cuando se envíe a la clase NSMutableArray). Vas a devolver la matriz de tu método, puedes usar esto como una forma abreviada de [[[NSMutableArray alloc] init] autorelease], pero es solo un atajo. Sin embargo, en muchas situaciones, puede crear un objeto con -init o +new y soltarlo manualmente en el momento apropiado.

4

Cocoa usa ciertas convenciones de nomenclatura. Todo lo que comience con alloc, new o copy devolverá algo con un retainCount de 1 y se le pedirá que lo suelte. Cualquier otra cosa que devuelva una función tiene un valor de retención retenido (puede ser retenido por otra cosa, o puede retenerse y liberarse).

Así:

NSMutableArray *removals = [NSMutableArray new]; 

Tiene un retainCount de 1, y:

NSMutableArray *removals = [NSMutableArray arrayWithCapacity:99]; 

o

NSMutableArray *removals = [NSMutableArray array]; 

No puesto que los métodos no están prefijadas con alloc, nuevo o dupdo. Esto está todo explicado en la gestión de memoria documentation. En particular:

Se toma la propiedad de un objeto si crea empleando un método cuyo nombre comienza con “alloc” o “nuevo” o contiene “copia” (por ejemplo, alloc, newObject, o mutableCopy), o si envía un mensaje de retención. Usted es responsable de renunciar propiedad de los objetos que posee utilizando versión o liberación automática. En cualquier otro momento usted recibe un objeto, no debe liberarlo .

+1

** Todos ** de esos métodos devuelven un objeto con un conteo de retención de 1. La diferencia es solo que con algunos posee el objeto y, por lo tanto, es necesario que lo libere, y con los demás no es el propietario. objeto y no están obligados a liberarlo (pero tampoco pueden contar con que esté más allá de la cadena de llamadas actual). – Chuck

+2

Estrictamente hablando, no, no lo hacen, es un detalle de implementación. En varios casos, devuelven cosas con diferentes recuentos de retención. Por ejemplo = [UIImage imageNamed:] puede devolver algo con un retazo grande de retención grande porque podría reutilizar una imagen en caché. –

+1

Bueno, sí, el recuento de retención en sí es un detalle de implementación. Los documentos de Apple dicen tanto. Y en todos los casos que mencionó anteriormente, en las versiones actuales de OS X, el valor de este detalle de implementación es 1. – Chuck

7

Esto es como las cosas implementados detrás de la escena:

+(NSMutableArray*) new 
{ 
    return [[NSMutableArray alloc] init]; 
} 

y

+(NSMutableArray*) arrayWithCapacity:(NSNumber)capacity 
{ 
    return [[NSMutableArray alloc] initWithCapacity:capacity] **autorelease**]; 
} 

En primer caso la matriz se asigna solamente y usted es responsable de de-asignación. Por el contrario, arrayWithCapacity se ha liberado automáticamente y no causará fugas, incluso si se olvida de desasignar.

Cuestiones relacionadas