• Català
  • Castellano
  • English


El Cicle de Vida d'un Objecte Cocoa


Un objecte cocoa existeix durant la seva vida, almenys potencialment, té fases diferents. És crea, s'inicialitza, s'usa (es a dir, altres objecte li envien missatges). Possiblement es reté, es copia o s'arxiva i eventualment s'allibera i es destrueix. El següent articles passa per la vida d'un objecte típic sense entrar massa en el detall.

Nota: Per un relat més detallat mireu "El Sistema d'Execució de l'Objective-C".

Començarem pel final, en la forma que els objectes es col·loquen. A diferència d'altres llenguatges de programació, l'Objective-C no té "recollidor de brossa" que facilitat que s'alliberin els objectes automàticament quan no es necessiten més. Per superar el consum del recollidor de brossa i la seva poca flexibilitat, Cocoa i l'Objective-C han optat per un procediment voluntari amb una política de realització per mantenir els objectes i disposar-ne fins que ja no siguin necessaris.

Aquest procediment i política es basen en el comptatge de referències. Cada objecte Cocoa duu un enter indicant el nombre d'altres objectes que els interessa la seva persistència. Aquest enter es refereix al comptador retain de l'objecte. Quan crees un objecte utilitzant les mètodes de classe alloc o allocWithZone:, Cocoa fa un parell de coses molt importants:

  • S'activa la variable d'instància isa de l'objecte -- la única variable d'instància pública de NSObject -- per apuntar a la classe del'objecte, integrant l'objecte dins de la vista del sistema d'execució de la jerarquia de classes. (Mire "Creació d'Objectes" per més informació).
  • Activa el comptador retain de l'objecte -- un tipus de variable d'instància ocult comú a tots els objectes -- a 1. (Aquí l'assumpció és que un creador d'objecte li interessa la seva persistència).

Després de l'assignació de l'objecte, normalment l'inicialitzaràs definits les seves variables d'instància a uns valors inicials raonables. (L'NSObject declara el mètode init com el prototip per a aquest propòsit). L'objecte ara està a punt per utilitzar-se; pots enviar-hi missatges, passar-lo a altres objectes, etc.

Nota: Com que un inicialitzador pugui retornar un altre objecte que ja s'ha assignat explícitament, la convenció és jerarquitzar l'expressió dels missatges amb el missatge alloc i el missatge init (o altres inicialitzadors) -- per exemple:

id unObj = [[LaMevaClasse alloc] init];

Quan alliberes un objecte -- que és, enviar-li un missatge release -- l'NSObject decrementa el seu comptador retain. Si el comptador retain cau de 1 a 0, l'objecte és des-assigna. La des-assignació es realitza en dues passes. Primer, s'invoca el missatge dealloc de l'objecte per alliberar les variables d'instància i alliberar dinàmicament la memòria assignada. Després el sistema operatiu destrueix el propi objecte i reclama la memòria ocupada de l'objecte.

Important: Mai hauries de cridar directament el mètode dealloc.

Què fas si no vols utilitzar més un objecte? Si després de crear un objecte li envies un missatge retain, el comptador retain de l'objecte s'incrementarà a 2. Ara es necessitaran els dos missatges release abans que succeeixi la des-assignació. La Figura 1 mostra aquest escenari tant simple.

Figura 1: El cicle de vida d'un objecte -- vista simplificada

Per descomptat, en aquest escenari el creador d'un objecte no té necessitat de retenir l'objecte. Aquest ja és el propietari de l'objecte. Però si aquest creador passa l'objecte a un altre objecte en un missatge, la situació canvia. En un programa en Objective-C, un objecte rebut d'algun altre objecte sempre s'assumeix dins de l'abast on s'ha obtingut. L'objecte rebut pot enviar missatges a un objecte receptor i pot passar-lo a altres objectes. Aquesta assumpció requereix que l'objecte que envia "es comporta" i no allibera prematurament l'objecte mentre un objecte client el referencia.

Si l'objecte client vol mantenir l'objecte rebut després que aquest surti de l'abast del programa, pot retenir-lo -- és a dir, enviar-li un missatge retain. Retenir un objecte incrementa el seu comptador de referències, de manera que expressa un membre propietari interessat en l'objecte. L'objecte client assumeix la responsabilitat d'alliberar l'objecte més endavant. Si el creador d'un objecte l'allibera, però un objecte client l'ha retingut, l'objecte persisteix fins que el client l'allibera. La Figura 2 il·lustra aquesta seqüència.

Figura 2: Retenint un objecte rebut

En comptes de retenir un objecte tu pots copiar-lo enviant-li un missatge copy i copyWithZone:. (Moltes, per no dir la majoria, sub-classes encapsulen alguns tipus tipus de dades que adopten o conformen aquest protocol). Copiar un objecte no només el duplica sinó que reseteja el seu comptador a 1 (mireu la Figura 3). La copia pot baixa o profunda, depenent de la naturalesa de l'objecte i el seu ús previst. Una copia profunda duplica els objectes mantinguts en les variables d'instància de l'objecte copiat mentre que una copia baixa només duplica les referències d'aquestes variables d'instància.

En termes d'ús, el que diferencia un copy de retain és la forma en que es crida l'objecte per l'ús únic del seu nou propietari; el nou propietari pot modificar l'objecte copiat sense respectar el seu original. Generalment copies un objecte en comptes de retenir-lo quan és un objecte valor -- és a dir, un objecte que encapsulen alguns valors primitius. Això és especialment cert quan aquest objecte és mutable, com en un NSMutableString. Per objecte immutables, copy i retain poden ser equivalent i podrien implementar-se de forma similar.

Figura 3: Copiant un objecte receptor

Potser has vist un problema potencial amb aquest esquema de gestionar el cicle de vida de l'objecte. Un objecte que crea un objecte i el passa a un altre objecte no pot saber quan pot alliberar-se sense problemes. Hi podrien haver múltiples referències a aquest objecte en la pila de crides, alguns per objectes desconeguts per l'objecte creador. Si l'objecte creador allibera l'objecte creat i llavors algun altres objecte envia un missatges a aquest objecte ara destruït, el programa podria caure. Per abordar aquest problema, Cocoa introdueix un mecanisme per deferir la des-assignació anomenada autoreleasing.

L'Autoreleasing fa ús dels pous d'autoalliberament (definits per la classe NSAutoreleasePool). Un pou d'autoalliberament és una col·lecció d'objectes dins un un abast definit explícitament que estan marcats per alliberaments eventuals. Els pous d'autoalliberament poden jerarquitzar-se. Quan envies a un objecte un missatge autorelease, una referència d'aquest objecte es posa dins del pou d'autoalliberament més immediat. Aquest manté un objecte vàlid, així altres objectes dins de l'abast definit pel pou d'autoalliberament pot enviar-li missatges. Quan el programa arriba al final del seu abast, el pou és alliberat i, com a conseqüència, tots els objectes també (mireu la Figura 4). Si estàs desenvolupant una aplicació pots no necessitar activar el pou d'autoalliberament; l'Application Kit automàticament n'activa un dins l'abast de l'event del cicle de l'aplicació.

Figura 4: Un pou d'autoalliberament

La discussió del cicle de vida d'objecte s'ha centrat fins ara en els mecanismes per gestionar els objectes a través d'aquest cicle. Però una política del propietari de l'objecte utilitza aquests mecanismes. Aquesta política pot resumir-se en el següent:

  • Si crees un objecte assignant-lo i inicialitzant-lo (per exemple, [[LaMevaClasse alloc] init], com a propietari de l'objecte tens la responsabilitat d'alliberar-lo. Aquesta regla també s'aplica si utilitzes el mètode de conveniència new de l'NSObject.
  • Si copies un objecte, com a propietari d'aquest ets el responsable d'alliberar-lo.
  • Si retens un objecte, com a propietari parcial de l'objecte, has d'alliberar-lo quan no el necessitis més.

Contràriament,

  • Si reps un objecte d'un altre objecte, al no ser-ne el propietari no has d'alliberar-lo. (Hi ha uns quantes excepcions d'aquesta regla, que estan explicitades en la documentació de referència).

Com en qualsevol conjunt de regles, hi ha excepcions:

  • Si crees un objecte utilitzant un mètode de la factoria de classe (com el mètode arrayWithCapacity: del NSMutableArray), s'assumeix que l'objecte que reps ha estat auto-alliberat (autoreleased). No has d'alliberar l'objecte tu mateix i has de retenir-lo si vols mantenir-lo més endavant.
  • +

  • Per evitar referències cícliques, un objecte fill mai reté al seu pare. (Un pare és qui crea al fill o és un objecte que manté el fill com una variable d'instància).

Nota: "Alliberar" en les línies anteriors significa que s'envia un missatge release o un missatge autorelease a un objecte..

Si no segueixes aquesta política de propietaris, et poden passar dues coses dolentes en el teu codi Cocoa. Doncs ni alliberar objectes creats, copiats o retinguts, perdràs memòria. O el teu programa tindrà errors dons enviarà un missatge a un objecte que dessassignat per tu. Hi ha una altra advertència: Eliminar aquests error pot portar molt de temps.

Un altre esdeveniment bàsic podria succeir durant el cicle de vida, és l'arxivat. L'arxivat converteix la xarxa d'objectes inter-relacionats que componen el programa orientat a objecte -- el graf d'objecte -- en una forma persistent (normalment un fitxer) que preserva la identitat i relacions de cada objecte en el graf. Quan un programa és des-arxivat, el seu graf d'objecte es reconstrueix des de l'arxiu. Per participar en l'arxivat i (des-arxivat), un objecte ha de ser capaç de codificar (i descodificar) les variables d'instància utilitzant els mètodes de la classe NSCoder. L'NSObject adopta el protocol NSCoding per aquest propòsit.

Aquesta descripció del cicle de vida dels objectes Cocoa grata la superfície del que s'ha de dir del tema. Mireu Gestió de la Memòria per informació més detallada.