2009-02-09 18 views
12

Soy nuevo en la codificación y trato de ponerme al día con Objective-C. Encontré un código que no entendí. Esperaba que alguien pudiera aclararlo para mí. En el caso siguiente, no estoy seguro de cómo funciona * foo2 y por qué no se está lanzando.punteros de Objective-C?

ClassOne *pointer = [[ClassOne alloc]init]; 

ClassTwo *foo = [[ClassTwo alloc]init], *foo2; 

foo2 = [foo add: pointer]; 
[foo release]; 
foo = foo2 

[pointer release]; 

[foo release]; 
+0

¿Cuáles son los nombres de clase de esas clases? –

+2

Dicho sea de paso, no estoy seguro de si esto sucedió en el proceso de anonimato del código o qué, pero ese código es bastante confuso considerando que solo son siete líneas. – Chuck

Respuesta

1

Porque no lo ha lanzado. Está lanzando referencias aquí, no liberando punteros. No es exactamente lo mismo. Cuando lo haces [foo release] la segunda vez, estás liberando la referencia foo que creaste cuando asignaste foo2 a foo.

Para liberar la referencia de foo2, debe llamar al release de esa referencia, no a una copia de la referencia.

3
ClassOne *pointer = [[ClassOne alloc]init]; 

La variable pointer es un puntero a un objeto de clase ClassOne. Le asignó el valor de un objeto recién creado.

ClassTwo *foo = [[ClassTwo alloc]init], *foo2; 

*foo y *foo2 son objetos de la clase ClassTwo. Solo foo se le asigna un objeto recién creado. foo2 puede señalar algo, por lo que no es seguro usarlo antes de asignarle un valor.

foo2 = [foo add: pointer]; 

foo2 se le asigna un valor de: Asumo que el mensaje add: de clase ClassTwo crea un objeto (la firma del método debe ser -(ClassTwo*)add:(ClassOne*);)

[foo release]; 

El objeto apuntado por foo hay más tiempo necesitado.

foo = foo2; 

La variable foo se le asigna el valor de foo2: ambos apuntan al mismo objeto.

[pointer release]; 

El objeto apuntado por pointer ya no se necesita.

[foo release]; 

El objeto apuntado por foo (y también por foo2) ya no se necesita.

+0

En realidad, la firma del método debe ser - (ClassTwo *) add: (ClassOne *); - en su ejemplo, el receptor es una instancia de ClassTwo, no la clase misma. –

+0

Sí, tienes razón! Actualizo mi respuesta, gracias. – mouviciel

1

De su simple ejemplo, es realmente difícil decir lo que está pasando. Típicamente, los métodos de tipo [class add:] devuelven el vacío, por lo que deben generar una advertencia del compilador de que "el valor nulo no se ignora como debería ser".

Así que sin más información, es un poco difícil de resolver.

Algunas cosas a tener en cuenta:

  • puede enviar comandos a las negativas en objc. Por lo tanto, si [foo add: pointer] devuelve nil, puede llamar a 'release' todo el día sin efecto.

  • retainCount es tu amigo. Puede invocarlo en cualquier objeto NSO para ver cuántos objetos se mantienen en él. Esto también puede ayudarlo a rastrear el problema.

  • Finalmente, ¿está la recolección de basura activada?

1

Eso realmente depende de qué [foo add:pointer]; lo haga. Parece que devuelve una copia de foo y lo conserva. Esto es claramente un mal diseño porque debería ser obvio por el método si el objeto devuelto es una copia/referencia. Los métodos con el nombre add: no deberían devolver una copia.

Paso a paso:

// this somehow creates a retained copy of foo. 
foo2 = [foo add:pointer]; 

// foo is released and gets destroyed. 
[foo release]; 

// makes foo point to the same object as foo2 
// (`foo` has no connection to the former object anymore) 
foo = foo2; 

// foo (and foo2 as they point to the same object) are released 
[foo release]; 
22

Con Objective-C Cocoa, estamos trabajando con gestión de memoria semiautomática de recuento de referencias. Al asignar memoria para un objeto, retener un objeto o llamar a un método copy en un objeto, el conteo de retención (recuento de referencia) se incrementa en 1. Cuando se llama al release en un objeto, se reduce el recuento de retención en uno. Al llamar al autorelease en un objeto, se llamará release al objeto en algún momento en el futuro (durante el ciclo de ejecución principal, cuando no se está ejecutando nada de su propio código, por lo que no extraerá la referencia de usted cuando usted estoy tratando de usarlo). Cuando el conteo de retención llega a 0, el objeto puede ser desasignado.

En general, si usted está llamando retain en un objeto, que estás señalando su interés en él, y que son responsables de hacer una llamada o releaseautorelease en algún momento, cuando ya no estás interesado en el objeto . Del mismo modo, si llama al alloc o un método copy en un objeto, ha señalado su interés en el objeto y debe coincidir con un release o autorelease en algún punto de la línea.

Este enlace cubre más o menos los usos directrices de Apple (y se debe utilizar) para la gestión de la memoria: Simple rules for memory management in Cocoa

Vamos a pasar por el código línea por línea:

ClassOne *pointer = [[ClassOne alloc]init]; 

pointer puntos a un objeto ClassOne recientemente asignado, con un conteo de retención de 1, ya que llamamos a alloc en él. Tenemos la responsabilidad de llamar al release o autorelease en pointer en algún momento en el futuro.

ClassTwo *foo = [[ClassTwo alloc]init], *foo2; 

foo apunta a un objeto ClassTwo recién asignada, con una cuenta de retención de 1, ya que llamamos alloc en él. Tenemos la responsabilidad de llamar al release o autorelease en foo en algún momento en el futuro.

foo2 no apunta a nada en particular en este momento. No es seguro de usar.

foo2 = [foo add: pointer]; 

pointer ha sido añadido a foo (lo que significa, que no sabemos la implementación). foo podría haber llamado retain en pointer para indicar su interés en él, y haberlo agregado como un campo, o podría haber agregado pointer a una colección (en cuyo caso es responsabilidad de la colección llamar al retain cuando se agrega un objeto, y release cuando se elimina un objeto). En cualquier caso, no afecta nuestro bloque de código, por lo que no nos importa lo que sucede bajo el capó

La referencia devuelta por este método podría ser pointer en sí, o podría ser una copia autorrevelada de pointer; no tenemos acceso a la API o la implementación para decirnos cuál.

En cualquier caso, no es nuestra responsabilidad llamar al release en este objeto. Si el método tenía copy en el nombre, o si llamamos al retain en la referencia devuelta (como foo2 = [[foo add:pointer] retain];), el recuento retenido se habría incrementado en 1, y habría sido nuestra responsabilidad llamar al release o autorelease.

[foo release]; 

El objeto referenciado por foo ha sido puesto en libertad, es decir, su conservan recuento se ha reducido en 1. Para este ejemplo, esto se empareja con la alloc llamada que hicimos en la línea 2, por lo que la cuenta de retención caerá a 0, lo que hace que foo sea liberado.

En general, sin embargo, no nos importa si el objeto ha sido desasignado o no; solo tenemos que asegurarnos de emparejar las llamadas alloc, copy o retain con el mismo número de llamadas release o . Si registramos un interés en un objeto en cualquier momento, es nuestra responsabilidad liberar nuestro interés, de lo contrario tendremos pérdidas de memoria.

foo = foo2; 

foo ahora apunta al mismo objeto referenciado por foo2. Recuerde, no hemos llamado a un alloc o copy método cuando obtuvimos foo2, ni registramos un interés en él llamando al retain. Como no tenemos la responsabilidad de llamar al release en foo2, no tenemos la responsabilidad de llamar al release en foo.

[pointer release]; 

pointer 's conservan recuento se ha reducido en 1. Esto puede haber llevado su recuento retener a 0 o no, depende de lo que foo hizo con ella cuando la añadimos. Aún así, no nos importa; hemos terminado nuestra responsabilidad al pointer llamando al release para que coincida con la llamada alloc que hicimos al principio. Aunque pointer todavía podría estar cerca después de esta llamada, no podemos hacer esa suposición, y tratar de hacer algo con el objeto referenciado anteriormente por el puntero sería un error (aunque podríamos cambiar pointer para apuntar a otra cosa libremente).

[foo release]; 

Si el autor de este código ha estado siguiendo las convenciones de gestión de memoria de Apple, entonces esto es innecesario. No tenemos la responsabilidad de llamar al release en foo o foo2 (apuntan al mismo objeto, recuerde). Esto no hará que el código se rompa; llamar a cualquier cosa con una referencia de nil es esencialmente una operación no operativa. Sin embargo, puede causar confusión para cualquiera que revise el código.

Ahora, el autor de este código puede haber roto las convenciones de administración de memoria. Pudo haber hecho que la llamada add devuelva una copia de pointer sin llamar al autorelease, en cuyo caso hace que la persona que llama sea responsable de llamar al release. Esta es una forma muy mala, y si se topa con un código que rompe la convención de administración de memoria, documente dónde lo usa y cómo rompe la convención para evitar confusiones en el futuro.

+3

El [lanzamiento de foo] al final probablemente * causará * un bloqueo si estamos siguiendo las pautas de administración de memoria. Se establece en el resultado de [foo add: puntero], que no sabemos es nulo. Si es algo distinto de cero, el programa se dispara. – Chuck

1

Wow, ¡Gracias a todos por las excelentes respuestas!

Creo que lo que realmente quiero decir es una referencia a un objeto y no a un puntero. Sin embargo, supongo que el , *foo2 anexado reside en la misma memoria que foo. También foo2 se libera de la memoria al mismo tiempo que foo. ¡Todavía tengo mucho más para apoyarme, pero un día a la vez!

Cuestiones relacionadas