2008-10-01 4 views
56

¿Cuál es la diferencia entre las 2 formas siguientes de asignar e iniciar un objeto?Asignar e iniciar objetos en el Objetivo C

AController *tempAController = [[AController alloc] init]; 
self.aController = tempAController; 
[tempAController release]; 

y

self.aController= [[AController alloc] init]; 

La mayor parte del ejemplo de manzana utilizar el primer método. ¿Por qué asignar, iniciar y objetar y luego liberar inmediatamente?

Respuesta

70

Cada objeto tiene un recuento de referencias. Cuando va a 0, el objeto se desasigna.

Suponiendo que la propiedad fue declarada como @property (retain):

Su primer ejemplo, línea por línea:

  1. El objeto es creado por alloc, tiene un contador de referencia de
  2. 1. El objeto
  3. se entrega al método selfsetAController:, que le envía un mensaje retain (porque el método no sabe de dónde viene el objeto), incrementando su recuento de referencia a 2.
  4. El código de llamada ya no necesita el objeto en sí mismo, por lo que llama release, decrementando el contador de referencias a 1.

Su segundo ejemplo hace básicamente los pasos 1 y 2, pero no 3, así que al final la referencia del objeto recuento es 2.

La regla es que si crea un objeto, usted es responsable de liberarlo cuando haya terminado con él. En su ejemplo, el código se hace con tempAController después de establecer la propiedad. Es responsabilidad del método setter llamar al retain si necesita que el objeto se quede.

Es importante recordar que self.property = foo; en Objective-C es en realidad la abreviatura de [self setProperty:foo]; y que el método setProperty: se va a retener o los objetos de copia según sea necesario.

Si la propiedad fue declarada @property (copy), entonces el objeto habría sido copiado en lugar de retenido. En el primer ejemplo, el objeto original sería lanzado de inmediato; en el segundo ejemplo, el recuento de referencia del objeto original sería 1, aunque debería ser 0. Por lo tanto, aún desea escribir el código de la misma manera.

Si la propiedad fue declarada @property (assign), entonces self no reclama la propiedad del objeto, y alguien más necesita retenerlo. En este caso, el primer ejemplo sería incorrecto. Este tipo de propiedades son raras, generalmente solo se usan para delegados de objeto.

+0

Este es sólo el caso si 'aController' se declara con el @property (retener), ¿verdad? –

+0

Eso es correcto. –

+0

También es el patrón correcto si la propiedad se declara (copiando). El segundo patrón es el correcto para una propiedad declarada (asignar) (o con la recolección de basura habilitada). –

5

Tenga en cuenta también que su deseo de reducir el código abajo a una línea es por eso que muchas personas usan Autorelease:

self.aController = [[[AController alloc] init] autorelease]; 

Aunque, en teoría, en la autorelease iPhone es de alguna manera más caro (nunca escuchado una explicación clara de por qué) y, por lo tanto, es posible que desee liberarlo explícitamente justo después de asignar el objeto a otra parte.

+0

No creo que sea más caro que la liberación automática en Cocoa. Solo que la liberación automática es más costosa que la liberación. Entonces, si puede liberar el objeto en lugar de autorrelleno, debería hacerlo. – schwa

+0

la liberación automática es más costosa porque tiene que encontrar una estructura de datos locales de subprocesos y agregarla (el grupo de liberación automática), mientras que la versión simplemente disminuye un número entero (el recuento de referencias). – benzado

+0

@benzado: Sí, la pregunta era por qué el autorelease es más caro ** en el iPhone **, no por qué es más caro que el lanzamiento. –

4

Otra cosa a tener en cuenta es que su ejemplo también depende de la definición @property de aController.

Si se definió como @property (readwrite, retain) id aController;, entonces su ejemplo funciona, mientras que si se define como @property (readwrite, assign) id aController;, la llamada extra a liberar causaría la desasignación de su objeto.

+0

No tiene sentido especificar readwrite, ya que es el predeterminado. – mk12

30

Como han notado otros, los dos fragmentos de código que muestra no son equivalentes (por razones de administración de memoria). En cuanto a por qué el primero se elige sobre este último:

La formulación correcta de este último sería

self.aController= [[[AController alloc] init] autorelease]; 

comparación con el anterior, esto implica una sobrecarga adicional a través de uso de la piscina autorelease, y en algunas las circunstancias llevarán a prolongar innecesariamente la vida útil del objeto (hasta que se libere el grupo de autorrelease), lo que aumentará la huella de memoria de su aplicación.

El otro "posible" aplicación (dependiendo de donde el ejemplo es de) es simplemente:

aController = [[AController alloc] init]; 

Sin embargo, establecer una variable de instancia directamente está fuertemente desanimado en otro lugar que en un método init o dealloc. En otros lugares, siempre debes usar métodos de acceso.

Esto nos lleva entonces a la aplicación se muestra en el código de ejemplo:

AController *tempAController = [[AController alloc] init]; 
self.aController = tempAController; 
[tempAController release]; 

Esto sigue las mejores prácticas desde:

  • Evita autorelease;
  • Hace que la semántica de gestión de memoria sea inmediatamente nítida;
  • Utiliza un método de acceso para establecer la variable de instancia.
+10

mmalc tiene razón. Pero ... en la práctica, 3 líneas de código para asignar a 1 variable es simplemente una locura. La mayoría de los equipos que he visto usan el enfoque de "hazlo todo en una sola línea de código" de autorrelleno, la sobrecarga es MUY pequeña. Si alguna vez tocas una situación en el iPhone en la que se produce una diferencia significativa en el rendimiento, probablemente deberías volver a escribir ese código en C de todos modos: estás haciendo demasiada asignación. Pocos equipos se apegarán a la convención "oficial" (con razón, en mi humilde opinión, los programas del mundo real deben ser claros, pero no hasta el punto de la diarrea verbal). – Adam

2

También puede hacer

@property (nonatomic, retain)AController *aController; 
... 
self.aController= [[AController alloc] init]; 
[aController release]; 

con una propiedad de retención, y que funcionaría de la misma manera, pero es mejor utilizar la otra forma (por retención de propiedades) porque es menos confuso, ese código hace que parezca que asigna un controlador y luego se elimina de la memoria, cuando en realidad no lo hace porque setAController lo conserva.

+0

Sería aún peor si se tratara de una propiedad copiadora. Liberarías la copia, dejarías un objeto muerto en la variable de instancia y filtrarías el objeto que desbloqueaste e ingresaste. –

+0

Pero funciona si se conserva. No es que debas hacer eso. – mk12

+0

De hecho, creo que este es un buen enfoque si sabes que la propiedad es retener. Esto elimina un poco de desorden de código. – andrewz

5

Si está utilizando Xcode, puede ayudarlo a detectar dicho código con el analizador estático. Sólo tiene que darle Construir >> construir y analizar

alt text

Esto le mostrará un mensaje muy útiles en este tipo de piezas de código.

alt text