¿Cómo se maneja la liberación para las propiedades de retención @synthesized?

Tengo algunas preguntas sobre las propiedades sintetizadas en Objective-C. La lista completa sigue, pero la pregunta básica es esta: ¿cómo garantiza el comstackdor que los ivars para las propiedades sintetizadas se lanzan correctamente, aunque mi código puede incluir o no métodos de publicación en dealloc?

Nota: Decidí no publicar estas preguntas como individuales porque están muy relacionadas y porque hay un puñado de preguntas existentes que abordan los problemas individuales sin llegar realmente al meollo del asunto.

Preguntas algo similares:

  • ¿La propiedad retener necesita una versión?
  • ¿Cuál es la diferencia entre propiedad y sintetizar?
  • Pregunta sobre retener atributo con propiedad y sintetizar

Configuración: considere una clase con una sola propiedad:

@interface Person : NSObject { NSString * name; } @property (nonatomic, retain) name; @end 

Pregunta # 1: El caso muy básico:

 @implementation Person @synthesize name; @end 

Con esta configuración, asumo que el name lanzará automáticamente cada vez que se suelte un objeto Person . En mi opinión, el comstackdor simplemente inserta [name release] en el método dealloc como si yo mismo lo hubiera escrito. ¿Es eso correcto?


Pregunta # 2: Si elijo escribir mi propio método dealloc para esta clase, y omito una llamada a [name release] , ¿se filtrará?

 @implementation Person @synthesize name; - (void)dealloc { [super dealloc]; } @end 

Pregunta n. ° 3: si elijo escribir mi propio método dealloc para esta clase, e dealloc una llamada a [name release] , ¿dará como resultado una versión doble, ya que @synthesize ya se ha ocupado de mí?

 @implementation Person @synthesize name; - (void)dealloc { [name release]; [super dealloc]; } @end 

Pregunta # 4: Si elijo escribir mi propio acceso a la propiedad para esta clase, pero no escribo mi propio método dealloc , ¿se dealloc name ?

 @implementation Person @dynamic name; - (void)setName:(NSString *)newName { [newName retain]; [name release]; name = newName; } @end 

Pregunta n. ° 5: tengo la sensación (basada en la experiencia) de que ninguno de los escenarios anteriores dará como resultado filtraciones o dobles lanzamientos, ya que el lenguaje ha sido diseñado para evitarlos. Eso, por supuesto, plantea la pregunta de “¿cómo?”. ¿Es el comstackdor simplemente lo suficientemente inteligente como para realizar un seguimiento de todos los casos posibles? ¿Qué pasaría si tuviera que hacer lo siguiente? (Tenga en cuenta que este es un ejemplo ridículo, solo para ilustrar mi punto):

 void Cleanup(id object) { [object release]; } @implementation Person @synthesize name; - (void)dealloc { Cleanup(name); } @end 

¿ dealloc al comstackdor para que agregue otro [name release] al método dealloc ?

Q1:

No. @synthesize no modifica el -dealloc por ti. Tienes que- -release el name tú mismo.

Q2:

Sí, se filtrará. La misma razón que Q1.

Q3:

No, no será de doble lanzamiento. La misma razón que Q1.

Q4:

Sí, se filtrará. La misma razón que Q1.

Q5:

No, no será de doble lanzamiento. La misma razón que Q1.


Puede verificar esto usted mismo -retain -dealloc y -dealloc y -dealloc para informar lo que está sucediendo.

 #import  @interface X : NSObject {} @end @implementation X -(oneway void)release { NSLog(@"Releasing %p, next count = %d", self, [self retainCount]-1); [super release]; } -(id)retain { NSLog(@"Retaining %p, next count = %d", self, [self retainCount]+1); return [super retain]; } -(void)dealloc { NSLog(@"Dealloc %p", self); [super dealloc]; } @end @interface Y : NSObject { X* x; } @property (nonatomic, retain) X* x; @end @implementation Y @synthesize x; - (void)dealloc { [x release]; [super dealloc]; } @end int main () { NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; Y* y = [[Y alloc] init]; X* x = [[X alloc] init]; yx = x; [y release]; [x release]; [pool drain]; return 0; } 

En Q1, Q2 y Q4, el último -retainCount de -retainCount de x es 1, por lo que hay una fuga, y en Q3 y Q5, el último -retainCount es 0 y se llama -dealloc , por lo que no hay pérdida.

De la documentación de Objective-C en propiedades :

dealloc

Las propiedades declaradas toman fundamentalmente el lugar de las declaraciones de métodos de acceso; cuando sintetiza una propiedad, el comstackdor solo crea los métodos de acceso ausentes. No hay interacción directa con el método dealloc: las propiedades no se liberan automáticamente. Las propiedades declaradas, sin embargo, proporcionan una forma útil de verificar la implementación de su método dealloc: puede buscar todas las declaraciones de propiedad en su archivo de encabezado y asegurarse de que las propiedades del objeto que no están marcadas assign se liberen, y las que se marcan asignar son no publicado.

Esto esencialmente responde todas tus preguntas.

La regla simple y general: si asigna, retiene o copia un objeto, USTED tiene que liberarlo.

Cuando utiliza la configuración semántica del colocador de retain en una instrucción @synthesize , le está pidiendo al comstackdor que @synthesize para usted un configurador que llama retain en el objeto. Nada más y nada menos. Y como está reteniendo ese objeto (aunque sea a través de un código autogenerado mágicamente), tiene que liberarlo, y dónde liberarlo está en -(void)dealloc .

Hay algo más que vale la pena saber: si tiene una propiedad sintetizada, establecer esa propiedad en cero (usando la syntax de punto, por supuesto) liberará el ivar por usted.