Pregunta

//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 haces [self.editMyObject release] en el método dealloc de la clase. self.editMyObject es de tipo MyObject. La segunda línea no incurre en ninguna pérdida de memoria. ¿La primera línea es incorrecta o hay una manera de liberar la memoria?

¿Fue útil?

Solución

El comportamiento correcto depende de la declaración de editMyObject @property. Suponiendo que esté deleced como

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

o

@property (copy) id editMyObject;

luego la asignación a través de self.editMyObject = retiene o copia el objeto asignado. Ya que [[MyObject alloc] init] devuelve un objeto retenido, que usted como el autor de la llamada, tiene una retención adicional de la instancia de MyObject y, por lo tanto, se filtrará a menos que exista una versión correspondiente (como en el segundo bloque). Le sugiero que lea el Programación de administración de memoria Guía [2].

Su segundo bloque de código es correcto, asumiendo que la propiedad está declarada como se describe anteriormente.

p.s. No debe utilizar [self.editMyObject release] en un método de -dealloc . Debe llamar a [editMyObject release] (suponiendo que el respaldo ivar de la propiedad de @ se llama editMyObject ). Llamar al descriptor de acceso (a través de self.editMyObject es seguro para los usuarios de acceso sintetizados, pero si un usuario de acceso anulado 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 al llamar al usuario.

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

Otros consejos

Su propiedad está declarada " retener " lo que significa que el objeto pasado se retiene automáticamente.

Debido a que su objeto ya tenía un recuento de referencia de uno de alloc / init, hay dos referencias y asumo que solo hay una versión (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];
}

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

Donde esto se complica es cuando asignas tu nuevo objeto a una propiedad en lugar de a una variable de instancia. La mayoría de las propiedades se definen como retain o copy , lo que significa que incrementan la cuenta de retención del objeto cuando se establece, o hacen una copia del objeto, dejando el original sin tocar.

En su ejemplo, probablemente tenga esto en su archivo .h :

@property (retain) MyObject *editMyObject;

Entonces en tu 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

Cuando creas tu nueva instancia de MyObject usando alloc / init , tiene un número retenido de uno. Cuando asigna la nueva instancia a self.editMyObject , en realidad está 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 tu 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

mantiene su nuevo objeto el tiempo suficiente para liberarlo, por lo que el recuento de retenciones está equilibrado (suponiendo que lo libere en su método dealloc ).

Consulte también Estrategia de cacao para la gestión de punteros / memoria

La primera versión crea un objeto sin una versión coincidente. Cuando asignas el objeto, significa que eres el propietario de ese objeto. Su establecedor presumiblemente retiene el objeto (como debería), lo que significa que ahora es dueño del objeto dos veces. Necesita la versión para equilibrar la creación del objeto.

Debería leer la guía de gestión de memoria Cocoa si planeas usar Cocoa en absoluto No es difícil una vez que lo aprendes, pero es algo que tienes que aprender o tendrás muchos problemas como este.

Todos los demás ya han explicado por qué causa una pérdida de memoria, por lo que solo intervengo sobre cómo evitar la variable 'temp' y aún así evitar una pérdida de memoria:

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

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

Se acordó y explicó que el siguiente código no tiene una fuga. (asumiendo que @property retiene y @synthesize para editMyObject):

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

Pregunta: ¿hay algún error con el siguiente código que no utiliza un puntero temporal?

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

Para mí, esto se ve bien.

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