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 release
autorelease
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.
¿Cuáles son los nombres de clase de esas clases? –
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