2009-03-04 19 views
8
//creates memory leak 
    self.editMyObject = [[MyObject alloc] init]; 

//does not create memory leak 
    MyObject *temp = [[MyObject alloc] init]; 
    self.editMyObject = temp; 
    [temp release]; 

La primera línea de código crea una pérdida de memoria, incluso si lo hace [self.editMyObject versión] en el método dealloc de la clase. self.editMyObject es del tipo MyObject. La segunda línea no genera pérdida de memoria. ¿La primera línea es incorrecta o hay alguna forma de liberar la memoria?¿Por qué esto crea una pérdida de memoria (iPhone)?

+0

Tres buenas respuestas. Tenga en cuenta a quién dar la respuesta también. – 4thSpace

Respuesta

10

El comportamiento correcto depende de la declaración de editMyObject @property. Asumiendo que es declararse no como

@property (retain) id editMyObject; //id may be replaced by a more specific type 

o

@property (copy) id editMyObject; 

luego asignación a través de self.editMyObject = conserva o copia el objeto asignado. Como [[MyObject alloc] init] devuelve un objeto retenido, que usted como el que llama posee, tiene un retener extra de la instancia de MyObject y, por lo tanto, se fugará a menos que haya una versión coincidente (como en el segundo bloque). Sugeriría que lees el Memory Management Programming Guide [2].

Su segundo bloque de código es correcto, suponiendo que la propiedad se declare como se describe arriba.

p.s. No debe usar [self.editMyObject release] en un método -dealloc. Debe llamar al [editMyObject release] (suponiendo que el respaldo ivar del @property se llama editMyObject). Llamar al descriptor de acceso (a través del self.editMyObject es seguro para los accesadores @synthesized, pero si un usuario anclado depende del estado del objeto (que puede no ser válido en la ubicación de la llamada en -dealloc o causa otros efectos secundarios, tiene un error llamando al acceso).

[2] las reglas de propiedad de objetos en Cocoa son muy simples: si se llama a un método que tiene alloc o copy en su firma (o utiliza +[NSObject new] que es básicamente equivalente a [[NSObject alloc] init]), entonces "propio" el objeto que se devuelve y debe equilibrar su adquisición de propiedad con release. En todos los demás casos, no es propietario del objeto devuelto por un método. Si desea conservarlo, debe tomar posesión con retain y, posteriormente, ser propietario con un release.

+0

Tengo un NSMutableArray. Lo configuré para copiar, pero cuando hago esto [self.List addObject: myobject], aparece una excepción no detectada. Ajustarlo para retener funciona bien. ¿Alguna sugerencia? – 4thSpace

+0

Creo que @property (copy) usará -copy para crear la copia, incluso si la nueva asignación es de tipo mutable.De este modo, obtiene una copia inmutable (normalmente usaría -mutableCopy para crear una copia mutable), lo que causaría la excepción cuando intente mutar el valor asignado ... –

+0

[cont'd] Puede usar @property (retener) y asignar como self.List = [[mutableArr mutableCopy] autorelease]. –

8

Su propiedad se declara "retener", lo que significa que el objeto pasado se conserva automáticamente.

Como su objeto ya tenía un recuento de referencia de uno de alloc/init, hay dos referencias y supongo que solo una (en su destructor).

Básicamente, la llamada a self.editMyObject realmente está haciendo esto;

-(void) setEditMyObject:(MyObject*)obj 
{ 
    if (editMyObject) 
    { 
    [editMyObject release]; 
    editMyObject = nil; 
    } 

    editMyObject = [obj retain]; 
} 
+0

excepto que si 'editMyObject == obj', esto podría fallar terriblemente, ya que estaría liberando el objeto (potencialmente desasignar ella), y luego tratar de mantener un puntero desasignado. –

3

La primera versión crea un objeto sin un comunicado de juego. Cuando asigna el objeto, significa que es propietario de ese objeto. Es probable que su setter conserve el objeto (como debería), lo que significa que ahora posee el objeto dos veces. Necesitas el lanzamiento para equilibrar la creación del objeto.

Debe leer the Cocoa memory management guide si planea utilizar Cocoa en absoluto. No es difícil una vez que lo aprendes, pero es algo que debes aprender o tendrás muchos problemas como este.

1

Todos los demás ya ha cubierto por la que provoca una pérdida de memoria, así que voy a meter su cuchara con la forma de evitar la 'temp' variable y aún evitar una pérdida de memoria:

self.editMyObject = [[[MyObject alloc] init] autorelease]; 

Esto dejará su (retener) la propiedad como único propietario del nuevo objeto. Exactamente el mismo resultado que su segundo ejemplo, pero sin el objeto temporal.

+0

En una aplicación de escritorio, generalmente hago esto. Sin embargo, en el iPhone (o en cualquier entorno con limitaciones de memoria), el uso de la función de liberación automática puede hacer que el uso de la memoria aumente innecesariamente. Quédese con retención/liberación a menos que necesite liberación automática. –

+0

Hmm. Definitivamente estoy viniendo del lado del escritorio en esto. ¿Puedes explicar qué causa el pico? Me parece que no se asigna memoria extra en este caso, pero la experiencia de codificación de mi iPhone es limitada. –

+0

No conozco los detalles, pero está bien documentado que la liberación automática es una llamada bastante cara en el iPhone. – 4thSpace

4

Por convención en Cocoa y Cocoa-touch, cualquier objeto creado usando [[SomeClass alloc] initX] o [SomeClass newX] se crea con un conteo de retención de uno. Usted es responsable de llamar [someClassInstance release] cuando haya terminado con su nueva instancia, por lo general en su método dealloc.

Cuando esto se complica es cuando se asigna el nuevo objeto a una propiedad en lugar de una variable de instancia. La mayoría de las propiedades se definen como retain o copy, lo que significa que o bien incrementan retener el recuento del objeto cuando se establece, o hacer una copia del objeto, dejando el original intacto.

En su ejemplo, es probable que tenga esto en su archivo .h:

@property (retain) MyObject *editMyObject; 

Así que en su primer ejemplo:

// (2) property setter increments retain count to 2 
self.editMyObject = 

    // (1) new object created with retain count of 1 
    [[MyObject alloc] init]; 

// oops! retain count is now 2 

Al crear su nueva instancia de MyObject usando alloc/init, tiene un conteo de retención de uno. Cuando se asigna la nueva instancia de self.editMyObject, en realidad estás llamando al método -setEditMyObject: que el compilador crea para usted cuando @synthesize editMyObject. Cuando el compilador ve self.editMyObject = x, lo reemplaza por [self setEditMyObject: x].

En el segundo ejemplo:

MyObject *temp = [[MyObject alloc] init]; 
// (1) new object created with retain count of 1 

self.editMyObject = temp; 
// (2) equivalent to [self setEditMyObject: temp]; 
// increments retain count to 2 

[temp release]; 
// (3) decrements retain count to 1 

se aferra a su nuevo objeto el tiempo suficiente para liberarlo, por lo que se equilibra la cuenta de retención (suponiendo que lo suelte en su método dealloc).

Véase también Cocoa strategy for pointer/memory management

0

Se acordó y ha explicado que el código de abajo no tiene una fuga (suponiendo @property retener y @synthesize para editMyObject):

//does not create memory leak 
MyObject *temp = [[MyObject alloc] init]; 
self.editMyObject = tempt; 
[temp release]; 

Pregunta: ¿es algo malo con el siguiente código que no usa un puntero temporal?

//does not create memory leak ? 
self.editMyObject = [[MyObject alloc] init]; 
[editMyObject release]; 

A mí esto se ve bien.

+0

que necesita para asegurarse de que su método de dealloc que el objeto se libera también: [liberación editMyObject] o self.editMyObject = nil; – jyavenard

Cuestiones relacionadas