El manejo de memoria es muy importante, sobre todo en dispositivos portátiles como el iPhone y el iPod, que no cuentan con los recursos tan grandes que tienen las computadoras de escritorio de ahora. Este tema es por mucho uno de los más importantes, entre otras razones, si haces un buen manejo de memoria, evitarás bugs, leaks, y crashes, que son una verdadera molestia y algunos son difíciles de debuggear.
En las lecciones anteriores ya he realizado algunas actividades del manejo de memoria, pero es un tema tan importante que merece su propia lección.
Retain y Release.
El contador de retain (retain count) se utiliza para llevar un registro de las referencias activas hacia un mismo objeto. Todos los objetos tienen internamete este contador, de tal manera que si el contador de retain es mayor a cero, significa que aún esta siendo utilizado en alguna parte del código, pero una vez que llega a cero, se debe liberar la memoria que estaba usando.
Cuando se quiere agregar una referencia al objeto se llama al método retain: [objeto retain]; esto aumenta el contador de retain en 1 y cuando deseamos remover una referencia llamaremos al método release: [objeto release]; esto disminuye el contador de retain en 1. Una vez que el contador llega a 0 (cero), se libera la memoria automáticamente con una llamada a [self dealloc]. Los métodos retain y release estan definidos en NSObject.
Algunas reglas generales son:
- Debes liberar con release todo lo que tu creaste, todo lo que es creado con alloc, o new, o los métodos que contienen la palabra copy (newObject, copyWithZone, mutableCopy,…), también debes darle release, si manualmente diste unretain.
- En otras palabras debe haber un (ni mas ni menos) release por cada retain, alloc, new o copy.
- Para liberar la memoria puedes usar tanto release, como autorelease, la diferencia es que release, disminuye el contador de retain inmediatamente, mientras que autorelease lo hará en un momento en el futuro.
- Un objeto que es recibido como retorno de algún método no debe ser liberado en el método receptor, por lo general esta garantizado que sea válido mientras dure el método que lo recibe e incluso puede ser devuelto al método que lo llamó. Aunque existen algunas excepciones como algunas aplicaciones multihilo, donde una combinación deretain y release o autorelease asegurarán que el objeto se mantenga válido mientras sea necesario.
- Si deseas conservar un objeto en una variable de instancia debes darle retain o copy, generalmente se hace a través de accesadores. (ver más abajo).
¿Cuándo hacer y cuando no hacer release?
Se debe llamar a release siempre cuando se tenga responsabilidad del objeto y ya no se necesite la referencia: Ej:
Correcto:
Correcto:
¿Cuándo hacer y cuando no hacer autorelease?
Se debe llamar a autorelease cuando se tenga responsabilidad del objeto y pero se necesite la referencia en un nivel superior de la pila de llamadas: Ej:
Correcto:
Correcto:
Al llamar a autorelease, se te garantiza que la referencia será válida incluso si la devuelves al método invocador,
Propiedades, accesadores (o accesores?) y notación de punto.
Las propiedades y accesores (o accesadores, como sea, jeje), tienen algunas particularidades en objective-c y son de mis características favoritas, ya que ayudan al encapsulamiento y le dan un estilo bastante elegante.
¿Que son accesadores (accessors)? Son métodos para leer y escribir propiedades (variables de instancia) de un objeto. El método para escribir, se le llama Setter, y al método para leer se llama Getter, y son definidos automáticamente por objective-c:
- A través del accesador podemos obtener acceso a las variables de instancia, tanto para escribir como para leer.
- Para definir los accesadores debemos declararlo primero con: @property (modificadores) TipoDeLaVariable *nombreDeVariable;
- @synthetize se usa para que objective-c implemente automáticamente los métodos.
Existen diferentes modificadores para las propiedades:
- readonly: muy claro, indica que solo se creará el método para leer, y no para escribir.
- atomic / nonatomic: atomic indica que se implementará una cerradura para impedir que múltiples métodos (en programas multihilo) accedan a la propiedad, nonatomic no implementa la cerradura.
- assign: Se implementa el método de setter de tal manera que solo se asigna: seria equivalente a algo como: -(void) setPropiedad: (id)valor { propiedad=valor; } es decir una asignación simple.
- copy: Si se especifica copy, el método setter, se implementará haciendo una copia del objeto asignado, al hacer esto se debe liberar con release ya que fué copiado (copy)
- retain: Al igual que con copy, retain aumenta el contador de retain en 1, por lo que debe ser liberado con release cuando sea adecuado. ¿Cuándo es adecuado?, sigue leyendo. La implementación que objective-c hace automáticamente, sería parecida a la siguiente:
-(void) setPropiedad: (id)valor {
if (propiedad=nil) { [propiedad release]; }
propiedad=[valor retain];
}. - Si se asigna un nuevo objeto a una propiedad marcada con retain, se libera cuando se asigna el nuevo objeto, pero, debes entender que no ocurre automáticamente sino cuando le asignas un nuevo objeto, por lo que el viejo es liberado, pero el nuevo no será liberado hasta que tu lo indiques. Lee a continuación:
Si utilizo copy o retain en la propiedad, ¿cuándo debo llamar a release? Dealloc!!
Depende de cada programa, pero sigue las mismas reglas, se libera cuando ya no se necesite, generalmente se libera cuando el objeto contenedor no es necesario, para esto utilizaremos el método especial dealloc.
El método dealloc es invocado automáticamente cuando el objeto va a morir, entonces ahí podemos liberar las propiedades que fueron marcadas con retain y copy.