2010-02-12 16 views
5

Recientemente, alguien desbordamiento de pila me dijo que el código de abajo no se escapa, que la propiedad se encarga de la retención en sí:iPhone: ¿Es esta una fuga o no

self.locationManager = [[CLLocationManager alloc] init]; 

en dealloc:

self.locationManager = nil; 

donde en el archivo .h:

@property (nonatomic, retain) CLLocationManager *locationManager; 

Pensé que era una fuga obvia y creía que esto debería arreglar la fuga:

self.locationManager = [[[CLLocationManager alloc] init] autorelease]; 

Sin embargo afirmó que no quiere trabajar, porque en sus palabras: "no AutoRelease propiedades de una clase. El descriptor de acceso autogenerado de una propiedad definida para retener manejará la retención automática"

Y él me hizo pensar si se equivoca o no han comprendí la gestión de memoria en absoluto

EDIT 1:? ¿El código

self.myName=[NSSting stringWithFormat:@"%@ is correct.", @"TechZen"]; 

diferente que

self.locationManager = [[[CLLocationManager alloc] init] autorelease]; 

memoria de gestión prudente?

El tipo dice que el primero es correcto y rechaza el segundo. ¿Por qué el segundo sería TAN INCORRECTO? Hasta donde puedo ver, ambos asignan instancias liberadas automáticamente a algunas propiedades, pero de alguna manera todavía hay un argumento obstinado de que el segundo está mal. No puedo verlo, cualquier ayuda sería tan bienvenida.

+1

En su EDIT 1, retenga-cuente sabiamente, son lo mismo. Esos objetos son retenidos solo una vez. Hay un autodesbloqueo implícito en TODAS las funciones de conveniencia. Si no ves la palabra alloc en el enunciado init, la variable se libera automáticamente. Si usa alloc, debería usar autorelease –

Respuesta

7

Contando conserva y libera ayuda en este caso. Definitivamente es una filtración. Su objeto locationManager se retendrá 2 veces: una vez por las llamadas alloc/init, y una vez por la propiedad. Establecer la propiedad en nil solo lanzará el locationManager una vez.

Para los ejemplos que figuran en Editar 1, de hecho son lo mismo. Parece que el otro desarrollador tiene una aversión a la autorrellena inmediata o no entiende lo que autorelease hace.

0

Agregue esta línea debajo ... Obviamente, le da el conteo de retención para locationManager. Esto le indicará si necesita liberarlo manualmente o no.

NSLog(@"retainCount:%d", [locationManager retainCount]); 

edición:

[locationManager release]; 
+0

No, no, estoy pidiendo conceptualmente. Además, NSlogging retainCounts no es una buena medida de fugas. –

+0

Oh, está bien. Mejor práctica, debe hacer esto en dealloc porque su objeto no se liberará cuando se vacíe el grupo de autorrelease. Editado arriba. – Zinc

+0

De la referencia de la clase NSObject: "Importante: este método [retainCount] generalmente no tiene ningún valor en la depuración de problemas de administración de memoria [...] es muy poco probable que pueda obtener información útil de este método." – jnic

1

@jnic: que son increíblemente mal. por lo que yo entiendo, el valor anterior de la propiedad se envía como un mensaje de liberación, no como el objeto que desea asignar a la propiedad. Así que, sí, el código propuesto se filtra y debe enviar un mensaje de liberación automática tal como lo pensó, ahmet emrah

4

La semántica de la conservan la opción de propiedad son:

  • el valor anterior (si lo hay) recibe un mensaje de liberación
  • el nuevo valor consigue un retienen mensaje

Por lo tanto, su instancia de CLLocationManager tendrá una retención de 2 después del setter. Uno de alloc y otro del setter de retención. Usted debe enviar un mensaje de liberación justo después de la incubadora:

CLLocationMamnager *aLocationManager = [[CLLocationManager alloc] init]; 
self.locationManager = aLocationManager; 
[aLocationManager release]; 

Alternativamente, añadirlo a la piscina autorelease de modo que al menos se free'd finalmente. Al igual que usted escribió a sí mismo:

self.locationManager = [[[CLLocationManager alloc] init] autorelease]; 

Aún mejor, no utilice la opción de retener la propiedad . Déjalo como asigne (el valor predeterminado) y está configurado para ir ya que está utilizando un objeto retenido de todos modos.

+0

Muchas gracias por la respuesta –

-2

Bien, este es el trato.

Cuando se define una propiedad como así ...

@property (nonAtomic, retain) NSString myName; 

... debido a los valores predeterminados de la propiedad Command su realidad como lo define como:

@property (nonAtomic, readwrite, retain, getter=getMyName,setter=setMyName) NSString myName; 

Cuando se utiliza @synthesize myName; detrás de escena, el compilador genera un método getter que se ve así:

-(void) setMyName:(NSString *) aString{ 
    if (!(myString==aString) { //test if a string is the same **object** as the current myString 
     if (aString != nil) { // if nil we don't want to send a retain 
      [aString retain]; // increment the retain count by one 
     }   
     [myString release]; //decrement the retain count of the currently assigned string by one. 
     myString=nil; //set the pointer to nil to ensure we don't point to the old string object 
     myString=aString; //assign the newly retained object to the myString symbol  
    } 
} 

Como puede ver, cualquier cadena de cualquier fuente, de cualquier recuento de retención anterior, liberado automáticamente o no, se retendrá automáticamente por el método en la asignación y cuando se asigna un nuevo valor, se liberará automáticamente por el método. Las asignaciones múltiples no acumularán el conteo retenido. Siempre que use el setter generado, el objeto asignado (en este caso aString) siempre tendrá un conteo de retención que lo mantendrá con vida en la clase.

Es por esto que usted puede hacer esto ...

self.myName=[NSSting stringWithFormat:@"%@ is correct.", @"TechZen"];

sin tener que hacer esto:

self.myName=[[NSSting stringWithFormat:@"%@ is correct.", @"TechZen"] retain]; 

... y no tener que preocuparse de si el valor de la cadena de repente desaparecen cuando los desagües autoreleasepool.

Sin embargo, si alguna vez llama ...

[self.myName release]; 

... cualquier lugar fuera del dealloc, entonces el objeto en la propiedad de mi ser nilled menos a seguirlo sin descanso. Por la misma razón, si llamas ...

[self.myName retain]; 

... en cualquier lugar, el objeto de la propiedad se escapará (posiblemente incluso después de que el objeto mismo se ha desasignado.)

Es por esto que digo que nunca para retener o AutoRelease cualquier objeto asignado o copiado en una propiedad. No solo es inútil sino contraproducente. De esto se deduce que solo desea ejecutar el lanzamiento en una propiedad cuando haya terminado con el objeto propio porque el seguimiento eficiente del recuento retenido por el colocador significa que puede anular la propiedad aunque aún pueda necesitarla.

Nunca se necesita autorización para una propiedad porque la propiedad siempre es retenida por el objeto y cualquier otro objeto debe gestionar la retención internamente si utiliza la propiedad del objeto propio.

Una vez que comprenda lo que sucede dentro de los descriptores de acceso generados, las reglas son obvias. Nunca retenga el objeto de una propiedad explícitamente. Nunca libere el objeto de una propiedad guardado en dealloc. Nunca desbloquees el objeto de una propiedad.

El corolario obvio de estas reglas es usar siempre las referencias de self.propertyName internamente en el objeto self para garantizar que la retención de la propiedad se administre automáticamente.

+0

Amigo, self.myName = [NSSting stringWithFormat: @"% @ is correct. ", @" TechZen "]; es EXACTAMENTE el mismo que self.locationManager = [[[CLLocationManager alloc] init] liberación automática]; gestión de memoria. No puedo creer cómo no se puede ver. –

2

la siguiente declaración es retenido dos veces, y como tal debe ser liberado dos veces:

self.locationManager = [[CLLocationManager alloc] init]; 

Este es probablemente el mejor escrito de la siguiente manera:

self.locationManager = [[[CLLocationManager alloc] init]autorelease]; 

Ahora, esta variable se ha conservado sólo una vez y puedes liberarlo en la función dealloc de tu clase.

Además, si ejecuta la siguiente línea de código, un lanzamiento se llama:

locationManager = nil; 

Desde locationManager se sintetiza, cuando la coloque a cero, que se publicó por primera vez.

Por otra parte, si se hizo lo siguiente, locationManager en primer lugar a conocer, a continuación, restablezca detrás de las escenas:

self.locationManager = foo; 

Por último, lo siguiente sería chocar con un EXC_BAD_ACCESS, ya que son de doble liberación locationManager cuando se establece a foo:

self.locationManager = [[[CLLocationManager alloc] init]autorelease]; 
[locationManager release]; 
self.locationManager = foo; 
1

simple regla de oro: si una propiedad está marcado como "mantener", siempre darle una variable autoreleased (si lo está creando) a menos que, por supuesto, también hay que retenerlo en otro lugar.

Cuestiones relacionadas